From 0b57ab730332dbf0033d652b4b531b2898c88039 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 13:34:22 +0200 Subject: [PATCH 01/54] cleaned up truncate vs truncate start --- crates/ai/src/models.rs | 37 ++++++++++++++----------- crates/ai/src/templates/base.rs | 33 ++++++++++++++-------- crates/ai/src/templates/file_context.rs | 12 +++++--- crates/ai/src/templates/generate.rs | 6 +++- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/crates/ai/src/models.rs b/crates/ai/src/models.rs index d0206cc41c..afb8496156 100644 --- a/crates/ai/src/models.rs +++ b/crates/ai/src/models.rs @@ -2,11 +2,20 @@ use anyhow::anyhow; use tiktoken_rs::CoreBPE; use util::ResultExt; +pub enum TruncationDirection { + Start, + End, +} + pub trait LanguageModel { fn name(&self) -> String; fn count_tokens(&self, content: &str) -> anyhow::Result; - fn truncate(&self, content: &str, length: usize) -> anyhow::Result; - fn truncate_start(&self, content: &str, length: usize) -> anyhow::Result; + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result; fn capacity(&self) -> anyhow::Result; } @@ -36,23 +45,19 @@ impl LanguageModel for OpenAILanguageModel { Err(anyhow!("bpe for open ai model was not retrieved")) } } - fn truncate(&self, content: &str, length: usize) -> anyhow::Result { + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result { if let Some(bpe) = &self.bpe { let tokens = bpe.encode_with_special_tokens(content); if tokens.len() > length { - bpe.decode(tokens[..length].to_vec()) - } else { - bpe.decode(tokens) - } - } else { - Err(anyhow!("bpe for open ai model was not retrieved")) - } - } - fn truncate_start(&self, content: &str, length: usize) -> anyhow::Result { - if let Some(bpe) = &self.bpe { - let tokens = bpe.encode_with_special_tokens(content); - if tokens.len() > length { - bpe.decode(tokens[length..].to_vec()) + match direction { + TruncationDirection::End => bpe.decode(tokens[..length].to_vec()), + TruncationDirection::Start => bpe.decode(tokens[length..].to_vec()), + } } else { bpe.decode(tokens) } diff --git a/crates/ai/src/templates/base.rs b/crates/ai/src/templates/base.rs index bda1d6c30e..e5ac414bc1 100644 --- a/crates/ai/src/templates/base.rs +++ b/crates/ai/src/templates/base.rs @@ -125,6 +125,8 @@ impl PromptChain { #[cfg(test)] pub(crate) mod tests { + use crate::models::TruncationDirection; + use super::*; #[test] @@ -141,7 +143,11 @@ pub(crate) mod tests { let mut token_count = args.model.count_tokens(&content)?; if let Some(max_token_length) = max_token_length { if token_count > max_token_length { - content = args.model.truncate(&content, max_token_length)?; + content = args.model.truncate( + &content, + max_token_length, + TruncationDirection::Start, + )?; token_count = max_token_length; } } @@ -162,7 +168,11 @@ pub(crate) mod tests { let mut token_count = args.model.count_tokens(&content)?; if let Some(max_token_length) = max_token_length { if token_count > max_token_length { - content = args.model.truncate(&content, max_token_length)?; + content = args.model.truncate( + &content, + max_token_length, + TruncationDirection::Start, + )?; token_count = max_token_length; } } @@ -183,19 +193,20 @@ pub(crate) mod tests { fn count_tokens(&self, content: &str) -> anyhow::Result { anyhow::Ok(content.chars().collect::>().len()) } - fn truncate(&self, content: &str, length: usize) -> anyhow::Result { - anyhow::Ok( - content.chars().collect::>()[..length] + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result { + anyhow::Ok(match direction { + TruncationDirection::End => content.chars().collect::>()[..length] .into_iter() .collect::(), - ) - } - fn truncate_start(&self, content: &str, length: usize) -> anyhow::Result { - anyhow::Ok( - content.chars().collect::>()[length..] + TruncationDirection::Start => content.chars().collect::>()[length..] .into_iter() .collect::(), - ) + }) } fn capacity(&self) -> anyhow::Result { anyhow::Ok(self.capacity) diff --git a/crates/ai/src/templates/file_context.rs b/crates/ai/src/templates/file_context.rs index 1afd61192e..1517134abb 100644 --- a/crates/ai/src/templates/file_context.rs +++ b/crates/ai/src/templates/file_context.rs @@ -3,6 +3,7 @@ use language::BufferSnapshot; use language::ToOffset; use crate::models::LanguageModel; +use crate::models::TruncationDirection; use crate::templates::base::PromptArguments; use crate::templates::base::PromptTemplate; use std::fmt::Write; @@ -70,8 +71,9 @@ fn retrieve_context( }; let truncated_start_window = - model.truncate_start(&start_window, start_goal_tokens)?; - let truncated_end_window = model.truncate(&end_window, end_goal_tokens)?; + model.truncate(&start_window, start_goal_tokens, TruncationDirection::Start)?; + let truncated_end_window = + model.truncate(&end_window, end_goal_tokens, TruncationDirection::End)?; writeln!( prompt, "{truncated_start_window}{selected_window}{truncated_end_window}" @@ -89,7 +91,7 @@ fn retrieve_context( if let Some(max_token_count) = max_token_count { if model.count_tokens(&prompt)? > max_token_count { truncated = true; - prompt = model.truncate(&prompt, max_token_count)?; + prompt = model.truncate(&prompt, max_token_count, TruncationDirection::End)?; } } } @@ -148,7 +150,9 @@ impl PromptTemplate for FileContext { // Really dumb truncation strategy if let Some(max_tokens) = max_token_length { - prompt = args.model.truncate(&prompt, max_tokens)?; + prompt = args + .model + .truncate(&prompt, max_tokens, TruncationDirection::End)?; } let token_count = args.model.count_tokens(&prompt)?; diff --git a/crates/ai/src/templates/generate.rs b/crates/ai/src/templates/generate.rs index 1eeb197f93..c9541c6b44 100644 --- a/crates/ai/src/templates/generate.rs +++ b/crates/ai/src/templates/generate.rs @@ -85,7 +85,11 @@ impl PromptTemplate for GenerateInlineContent { // Really dumb truncation strategy if let Some(max_tokens) = max_token_length { - prompt = args.model.truncate(&prompt, max_tokens)?; + prompt = args.model.truncate( + &prompt, + max_tokens, + crate::models::TruncationDirection::End, + )?; } let token_count = args.model.count_tokens(&prompt)?; From a62baf34f2e3bef619ba57e557cb30baa6356b29 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 13:46:49 +0200 Subject: [PATCH 02/54] rename templates to prompts in ai crate --- crates/ai/src/ai.rs | 2 +- crates/ai/src/{templates => prompts}/base.rs | 2 +- crates/ai/src/{templates => prompts}/file_context.rs | 4 ++-- crates/ai/src/{templates => prompts}/generate.rs | 2 +- crates/ai/src/{templates => prompts}/mod.rs | 0 crates/ai/src/{templates => prompts}/preamble.rs | 2 +- .../src/{templates => prompts}/repository_context.rs | 2 +- crates/assistant/src/assistant_panel.rs | 2 +- crates/assistant/src/prompts.rs | 10 +++++----- 9 files changed, 13 insertions(+), 13 deletions(-) rename crates/ai/src/{templates => prompts}/base.rs (99%) rename crates/ai/src/{templates => prompts}/file_context.rs (98%) rename crates/ai/src/{templates => prompts}/generate.rs (97%) rename crates/ai/src/{templates => prompts}/mod.rs (100%) rename crates/ai/src/{templates => prompts}/preamble.rs (95%) rename crates/ai/src/{templates => prompts}/repository_context.rs (98%) diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index f168c15793..c0b78b74cf 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,4 +1,4 @@ pub mod completion; pub mod embedding; pub mod models; -pub mod templates; +pub mod prompts; diff --git a/crates/ai/src/templates/base.rs b/crates/ai/src/prompts/base.rs similarity index 99% rename from crates/ai/src/templates/base.rs rename to crates/ai/src/prompts/base.rs index e5ac414bc1..f0ff597e63 100644 --- a/crates/ai/src/templates/base.rs +++ b/crates/ai/src/prompts/base.rs @@ -6,7 +6,7 @@ use language::BufferSnapshot; use util::ResultExt; use crate::models::LanguageModel; -use crate::templates::repository_context::PromptCodeSnippet; +use crate::prompts::repository_context::PromptCodeSnippet; pub(crate) enum PromptFileType { Text, diff --git a/crates/ai/src/templates/file_context.rs b/crates/ai/src/prompts/file_context.rs similarity index 98% rename from crates/ai/src/templates/file_context.rs rename to crates/ai/src/prompts/file_context.rs index 1517134abb..f108a62f6f 100644 --- a/crates/ai/src/templates/file_context.rs +++ b/crates/ai/src/prompts/file_context.rs @@ -4,8 +4,8 @@ use language::ToOffset; use crate::models::LanguageModel; use crate::models::TruncationDirection; -use crate::templates::base::PromptArguments; -use crate::templates::base::PromptTemplate; +use crate::prompts::base::PromptArguments; +use crate::prompts::base::PromptTemplate; use std::fmt::Write; use std::ops::Range; use std::sync::Arc; diff --git a/crates/ai/src/templates/generate.rs b/crates/ai/src/prompts/generate.rs similarity index 97% rename from crates/ai/src/templates/generate.rs rename to crates/ai/src/prompts/generate.rs index c9541c6b44..c7be620107 100644 --- a/crates/ai/src/templates/generate.rs +++ b/crates/ai/src/prompts/generate.rs @@ -1,4 +1,4 @@ -use crate::templates::base::{PromptArguments, PromptFileType, PromptTemplate}; +use crate::prompts::base::{PromptArguments, PromptFileType, PromptTemplate}; use anyhow::anyhow; use std::fmt::Write; diff --git a/crates/ai/src/templates/mod.rs b/crates/ai/src/prompts/mod.rs similarity index 100% rename from crates/ai/src/templates/mod.rs rename to crates/ai/src/prompts/mod.rs diff --git a/crates/ai/src/templates/preamble.rs b/crates/ai/src/prompts/preamble.rs similarity index 95% rename from crates/ai/src/templates/preamble.rs rename to crates/ai/src/prompts/preamble.rs index 9eabaaeb97..92e0edeb78 100644 --- a/crates/ai/src/templates/preamble.rs +++ b/crates/ai/src/prompts/preamble.rs @@ -1,4 +1,4 @@ -use crate::templates::base::{PromptArguments, PromptFileType, PromptTemplate}; +use crate::prompts::base::{PromptArguments, PromptFileType, PromptTemplate}; use std::fmt::Write; pub struct EngineerPreamble {} diff --git a/crates/ai/src/templates/repository_context.rs b/crates/ai/src/prompts/repository_context.rs similarity index 98% rename from crates/ai/src/templates/repository_context.rs rename to crates/ai/src/prompts/repository_context.rs index a8e7f4b5af..c21b0f995c 100644 --- a/crates/ai/src/templates/repository_context.rs +++ b/crates/ai/src/prompts/repository_context.rs @@ -1,4 +1,4 @@ -use crate::templates::base::{PromptArguments, PromptTemplate}; +use crate::prompts::base::{PromptArguments, PromptTemplate}; use std::fmt::Write; use std::{ops::Range, path::PathBuf}; diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index ca8c54a285..64eff04b8d 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -9,7 +9,7 @@ use ai::{ completion::{ stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, }, - templates::repository_context::PromptCodeSnippet, + prompts::repository_context::PromptCodeSnippet, }; use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; diff --git a/crates/assistant/src/prompts.rs b/crates/assistant/src/prompts.rs index dffcbc2923..8fff232fdb 100644 --- a/crates/assistant/src/prompts.rs +++ b/crates/assistant/src/prompts.rs @@ -1,9 +1,9 @@ use ai::models::{LanguageModel, OpenAILanguageModel}; -use ai::templates::base::{PromptArguments, PromptChain, PromptPriority, PromptTemplate}; -use ai::templates::file_context::FileContext; -use ai::templates::generate::GenerateInlineContent; -use ai::templates::preamble::EngineerPreamble; -use ai::templates::repository_context::{PromptCodeSnippet, RepositoryContext}; +use ai::prompts::base::{PromptArguments, PromptChain, PromptPriority, PromptTemplate}; +use ai::prompts::file_context::FileContext; +use ai::prompts::generate::GenerateInlineContent; +use ai::prompts::preamble::EngineerPreamble; +use ai::prompts::repository_context::{PromptCodeSnippet, RepositoryContext}; use language::{BufferSnapshot, OffsetRangeExt, ToOffset}; use std::cmp::{self, Reverse}; use std::ops::Range; From 3712794e561b7aa6068ee3de0fc411d5cb311566 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 13:47:28 +0200 Subject: [PATCH 03/54] move OpenAILanguageModel to providers folder --- crates/ai/src/ai.rs | 1 + crates/ai/src/models.rs | 55 ----------------------- crates/ai/src/providers/mod.rs | 1 + crates/ai/src/providers/open_ai/mod.rs | 2 + crates/ai/src/providers/open_ai/model.rs | 56 ++++++++++++++++++++++++ crates/assistant/src/prompts.rs | 3 +- 6 files changed, 62 insertions(+), 56 deletions(-) create mode 100644 crates/ai/src/providers/mod.rs create mode 100644 crates/ai/src/providers/open_ai/mod.rs create mode 100644 crates/ai/src/providers/open_ai/model.rs diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index c0b78b74cf..a3ae2fcf7f 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -2,3 +2,4 @@ pub mod completion; pub mod embedding; pub mod models; pub mod prompts; +pub mod providers; diff --git a/crates/ai/src/models.rs b/crates/ai/src/models.rs index afb8496156..1db3d58c6f 100644 --- a/crates/ai/src/models.rs +++ b/crates/ai/src/models.rs @@ -1,7 +1,3 @@ -use anyhow::anyhow; -use tiktoken_rs::CoreBPE; -use util::ResultExt; - pub enum TruncationDirection { Start, End, @@ -18,54 +14,3 @@ pub trait LanguageModel { ) -> anyhow::Result; fn capacity(&self) -> anyhow::Result; } - -pub struct OpenAILanguageModel { - name: String, - bpe: Option, -} - -impl OpenAILanguageModel { - pub fn load(model_name: &str) -> Self { - let bpe = tiktoken_rs::get_bpe_from_model(model_name).log_err(); - OpenAILanguageModel { - name: model_name.to_string(), - bpe, - } - } -} - -impl LanguageModel for OpenAILanguageModel { - fn name(&self) -> String { - self.name.clone() - } - fn count_tokens(&self, content: &str) -> anyhow::Result { - if let Some(bpe) = &self.bpe { - anyhow::Ok(bpe.encode_with_special_tokens(content).len()) - } else { - Err(anyhow!("bpe for open ai model was not retrieved")) - } - } - fn truncate( - &self, - content: &str, - length: usize, - direction: TruncationDirection, - ) -> anyhow::Result { - if let Some(bpe) = &self.bpe { - let tokens = bpe.encode_with_special_tokens(content); - if tokens.len() > length { - match direction { - TruncationDirection::End => bpe.decode(tokens[..length].to_vec()), - TruncationDirection::Start => bpe.decode(tokens[length..].to_vec()), - } - } else { - bpe.decode(tokens) - } - } else { - Err(anyhow!("bpe for open ai model was not retrieved")) - } - } - fn capacity(&self) -> anyhow::Result { - anyhow::Ok(tiktoken_rs::model::get_context_size(&self.name)) - } -} diff --git a/crates/ai/src/providers/mod.rs b/crates/ai/src/providers/mod.rs new file mode 100644 index 0000000000..acd0f9d910 --- /dev/null +++ b/crates/ai/src/providers/mod.rs @@ -0,0 +1 @@ +pub mod open_ai; diff --git a/crates/ai/src/providers/open_ai/mod.rs b/crates/ai/src/providers/open_ai/mod.rs new file mode 100644 index 0000000000..8d8489e187 --- /dev/null +++ b/crates/ai/src/providers/open_ai/mod.rs @@ -0,0 +1,2 @@ +pub mod model; +pub use model::OpenAILanguageModel; diff --git a/crates/ai/src/providers/open_ai/model.rs b/crates/ai/src/providers/open_ai/model.rs new file mode 100644 index 0000000000..42523f3df4 --- /dev/null +++ b/crates/ai/src/providers/open_ai/model.rs @@ -0,0 +1,56 @@ +use anyhow::anyhow; +use tiktoken_rs::CoreBPE; +use util::ResultExt; + +use crate::models::{LanguageModel, TruncationDirection}; + +pub struct OpenAILanguageModel { + name: String, + bpe: Option, +} + +impl OpenAILanguageModel { + pub fn load(model_name: &str) -> Self { + let bpe = tiktoken_rs::get_bpe_from_model(model_name).log_err(); + OpenAILanguageModel { + name: model_name.to_string(), + bpe, + } + } +} + +impl LanguageModel for OpenAILanguageModel { + fn name(&self) -> String { + self.name.clone() + } + fn count_tokens(&self, content: &str) -> anyhow::Result { + if let Some(bpe) = &self.bpe { + anyhow::Ok(bpe.encode_with_special_tokens(content).len()) + } else { + Err(anyhow!("bpe for open ai model was not retrieved")) + } + } + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result { + if let Some(bpe) = &self.bpe { + let tokens = bpe.encode_with_special_tokens(content); + if tokens.len() > length { + match direction { + TruncationDirection::End => bpe.decode(tokens[..length].to_vec()), + TruncationDirection::Start => bpe.decode(tokens[length..].to_vec()), + } + } else { + bpe.decode(tokens) + } + } else { + Err(anyhow!("bpe for open ai model was not retrieved")) + } + } + fn capacity(&self) -> anyhow::Result { + anyhow::Ok(tiktoken_rs::model::get_context_size(&self.name)) + } +} diff --git a/crates/assistant/src/prompts.rs b/crates/assistant/src/prompts.rs index 8fff232fdb..25af023c40 100644 --- a/crates/assistant/src/prompts.rs +++ b/crates/assistant/src/prompts.rs @@ -1,9 +1,10 @@ -use ai::models::{LanguageModel, OpenAILanguageModel}; +use ai::models::LanguageModel; use ai::prompts::base::{PromptArguments, PromptChain, PromptPriority, PromptTemplate}; use ai::prompts::file_context::FileContext; use ai::prompts::generate::GenerateInlineContent; use ai::prompts::preamble::EngineerPreamble; use ai::prompts::repository_context::{PromptCodeSnippet, RepositoryContext}; +use ai::providers::open_ai::OpenAILanguageModel; use language::{BufferSnapshot, OffsetRangeExt, ToOffset}; use std::cmp::{self, Reverse}; use std::ops::Range; From 05ae978cb773978fcb16c81d14e8b4cd4907decd Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 13:57:13 +0200 Subject: [PATCH 04/54] move OpenAICompletionProvider to providers location --- crates/ai/src/completion.rs | 209 +----------------- crates/ai/src/providers/open_ai/completion.rs | 209 ++++++++++++++++++ crates/ai/src/providers/open_ai/mod.rs | 2 + crates/assistant/src/assistant.rs | 2 +- crates/assistant/src/assistant_panel.rs | 10 +- crates/assistant/src/codegen.rs | 3 +- 6 files changed, 222 insertions(+), 213 deletions(-) create mode 100644 crates/ai/src/providers/open_ai/completion.rs diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index de6ce9da71..f45893898f 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -1,177 +1,7 @@ -use anyhow::{anyhow, Result}; -use futures::{ - future::BoxFuture, io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, FutureExt, - Stream, StreamExt, -}; -use gpui::executor::Background; -use isahc::{http::StatusCode, Request, RequestExt}; -use serde::{Deserialize, Serialize}; -use std::{ - fmt::{self, Display}, - io, - sync::Arc, -}; +use anyhow::Result; +use futures::{future::BoxFuture, stream::BoxStream}; -pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; - -#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum Role { - User, - Assistant, - System, -} - -impl Role { - pub fn cycle(&mut self) { - *self = match self { - Role::User => Role::Assistant, - Role::Assistant => Role::System, - Role::System => Role::User, - } - } -} - -impl Display for Role { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Role::User => write!(f, "User"), - Role::Assistant => write!(f, "Assistant"), - Role::System => write!(f, "System"), - } - } -} - -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] -pub struct RequestMessage { - pub role: Role, - pub content: String, -} - -#[derive(Debug, Default, Serialize)] -pub struct OpenAIRequest { - pub model: String, - pub messages: Vec, - pub stream: bool, - pub stop: Vec, - pub temperature: f32, -} - -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] -pub struct ResponseMessage { - pub role: Option, - pub content: Option, -} - -#[derive(Deserialize, Debug)] -pub struct OpenAIUsage { - pub prompt_tokens: u32, - pub completion_tokens: u32, - pub total_tokens: u32, -} - -#[derive(Deserialize, Debug)] -pub struct ChatChoiceDelta { - pub index: u32, - pub delta: ResponseMessage, - pub finish_reason: Option, -} - -#[derive(Deserialize, Debug)] -pub struct OpenAIResponseStreamEvent { - pub id: Option, - pub object: String, - pub created: u32, - pub model: String, - pub choices: Vec, - pub usage: Option, -} - -pub async fn stream_completion( - api_key: String, - executor: Arc, - mut request: OpenAIRequest, -) -> Result>> { - request.stream = true; - - let (tx, rx) = futures::channel::mpsc::unbounded::>(); - - let json_data = serde_json::to_string(&request)?; - let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions")) - .header("Content-Type", "application/json") - .header("Authorization", format!("Bearer {}", api_key)) - .body(json_data)? - .send_async() - .await?; - - let status = response.status(); - if status == StatusCode::OK { - executor - .spawn(async move { - let mut lines = BufReader::new(response.body_mut()).lines(); - - fn parse_line( - line: Result, - ) -> Result> { - if let Some(data) = line?.strip_prefix("data: ") { - let event = serde_json::from_str(&data)?; - Ok(Some(event)) - } else { - Ok(None) - } - } - - while let Some(line) = lines.next().await { - if let Some(event) = parse_line(line).transpose() { - let done = event.as_ref().map_or(false, |event| { - event - .choices - .last() - .map_or(false, |choice| choice.finish_reason.is_some()) - }); - if tx.unbounded_send(event).is_err() { - break; - } - - if done { - break; - } - } - } - - anyhow::Ok(()) - }) - .detach(); - - Ok(rx) - } else { - let mut body = String::new(); - response.body_mut().read_to_string(&mut body).await?; - - #[derive(Deserialize)] - struct OpenAIResponse { - error: OpenAIError, - } - - #[derive(Deserialize)] - struct OpenAIError { - message: String, - } - - match serde_json::from_str::(&body) { - Ok(response) if !response.error.message.is_empty() => Err(anyhow!( - "Failed to connect to OpenAI API: {}", - response.error.message, - )), - - _ => Err(anyhow!( - "Failed to connect to OpenAI API: {} {}", - response.status(), - body, - )), - } - } -} +use crate::providers::open_ai::completion::OpenAIRequest; pub trait CompletionProvider { fn complete( @@ -179,36 +9,3 @@ pub trait CompletionProvider { prompt: OpenAIRequest, ) -> BoxFuture<'static, Result>>>; } - -pub struct OpenAICompletionProvider { - api_key: String, - executor: Arc, -} - -impl OpenAICompletionProvider { - pub fn new(api_key: String, executor: Arc) -> Self { - Self { api_key, executor } - } -} - -impl CompletionProvider for OpenAICompletionProvider { - fn complete( - &self, - prompt: OpenAIRequest, - ) -> BoxFuture<'static, Result>>> { - let request = stream_completion(self.api_key.clone(), self.executor.clone(), prompt); - async move { - let response = request.await?; - let stream = response - .filter_map(|response| async move { - match response { - Ok(mut response) => Some(Ok(response.choices.pop()?.delta.content?)), - Err(error) => Some(Err(error)), - } - }) - .boxed(); - Ok(stream) - } - .boxed() - } -} diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs new file mode 100644 index 0000000000..bb6138eee3 --- /dev/null +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -0,0 +1,209 @@ +use anyhow::{anyhow, Result}; +use futures::{ + future::BoxFuture, io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, FutureExt, + Stream, StreamExt, +}; +use gpui::executor::Background; +use isahc::{http::StatusCode, Request, RequestExt}; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{self, Display}, + io, + sync::Arc, +}; + +use crate::completion::CompletionProvider; + +pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; + +#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum Role { + User, + Assistant, + System, +} + +impl Role { + pub fn cycle(&mut self) { + *self = match self { + Role::User => Role::Assistant, + Role::Assistant => Role::System, + Role::System => Role::User, + } + } +} + +impl Display for Role { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Role::User => write!(f, "User"), + Role::Assistant => write!(f, "Assistant"), + Role::System => write!(f, "System"), + } + } +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +pub struct RequestMessage { + pub role: Role, + pub content: String, +} + +#[derive(Debug, Default, Serialize)] +pub struct OpenAIRequest { + pub model: String, + pub messages: Vec, + pub stream: bool, + pub stop: Vec, + pub temperature: f32, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +pub struct ResponseMessage { + pub role: Option, + pub content: Option, +} + +#[derive(Deserialize, Debug)] +pub struct OpenAIUsage { + pub prompt_tokens: u32, + pub completion_tokens: u32, + pub total_tokens: u32, +} + +#[derive(Deserialize, Debug)] +pub struct ChatChoiceDelta { + pub index: u32, + pub delta: ResponseMessage, + pub finish_reason: Option, +} + +#[derive(Deserialize, Debug)] +pub struct OpenAIResponseStreamEvent { + pub id: Option, + pub object: String, + pub created: u32, + pub model: String, + pub choices: Vec, + pub usage: Option, +} + +pub async fn stream_completion( + api_key: String, + executor: Arc, + mut request: OpenAIRequest, +) -> Result>> { + request.stream = true; + + let (tx, rx) = futures::channel::mpsc::unbounded::>(); + + let json_data = serde_json::to_string(&request)?; + let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions")) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", api_key)) + .body(json_data)? + .send_async() + .await?; + + let status = response.status(); + if status == StatusCode::OK { + executor + .spawn(async move { + let mut lines = BufReader::new(response.body_mut()).lines(); + + fn parse_line( + line: Result, + ) -> Result> { + if let Some(data) = line?.strip_prefix("data: ") { + let event = serde_json::from_str(&data)?; + Ok(Some(event)) + } else { + Ok(None) + } + } + + while let Some(line) = lines.next().await { + if let Some(event) = parse_line(line).transpose() { + let done = event.as_ref().map_or(false, |event| { + event + .choices + .last() + .map_or(false, |choice| choice.finish_reason.is_some()) + }); + if tx.unbounded_send(event).is_err() { + break; + } + + if done { + break; + } + } + } + + anyhow::Ok(()) + }) + .detach(); + + Ok(rx) + } else { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + #[derive(Deserialize)] + struct OpenAIResponse { + error: OpenAIError, + } + + #[derive(Deserialize)] + struct OpenAIError { + message: String, + } + + match serde_json::from_str::(&body) { + Ok(response) if !response.error.message.is_empty() => Err(anyhow!( + "Failed to connect to OpenAI API: {}", + response.error.message, + )), + + _ => Err(anyhow!( + "Failed to connect to OpenAI API: {} {}", + response.status(), + body, + )), + } + } +} + +pub struct OpenAICompletionProvider { + api_key: String, + executor: Arc, +} + +impl OpenAICompletionProvider { + pub fn new(api_key: String, executor: Arc) -> Self { + Self { api_key, executor } + } +} + +impl CompletionProvider for OpenAICompletionProvider { + fn complete( + &self, + prompt: OpenAIRequest, + ) -> BoxFuture<'static, Result>>> { + let request = stream_completion(self.api_key.clone(), self.executor.clone(), prompt); + async move { + let response = request.await?; + let stream = response + .filter_map(|response| async move { + match response { + Ok(mut response) => Some(Ok(response.choices.pop()?.delta.content?)), + Err(error) => Some(Err(error)), + } + }) + .boxed(); + Ok(stream) + } + .boxed() + } +} diff --git a/crates/ai/src/providers/open_ai/mod.rs b/crates/ai/src/providers/open_ai/mod.rs index 8d8489e187..26f3068ca1 100644 --- a/crates/ai/src/providers/open_ai/mod.rs +++ b/crates/ai/src/providers/open_ai/mod.rs @@ -1,2 +1,4 @@ +pub mod completion; pub mod model; +pub use completion::*; pub use model::OpenAILanguageModel; diff --git a/crates/assistant/src/assistant.rs b/crates/assistant/src/assistant.rs index 6c9b14333e..91d61a19f9 100644 --- a/crates/assistant/src/assistant.rs +++ b/crates/assistant/src/assistant.rs @@ -4,7 +4,7 @@ mod codegen; mod prompts; mod streaming_diff; -use ai::completion::Role; +use ai::providers::open_ai::Role; use anyhow::Result; pub use assistant_panel::AssistantPanel; use assistant_settings::OpenAIModel; diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 64eff04b8d..9b749e5091 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -5,12 +5,12 @@ use crate::{ MessageId, MessageMetadata, MessageStatus, Role, SavedConversation, SavedConversationMetadata, SavedMessage, }; -use ai::{ - completion::{ - stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, - }, - prompts::repository_context::PromptCodeSnippet, + +use ai::providers::open_ai::{ + stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, }; + +use ai::prompts::repository_context::PromptCodeSnippet; use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; use client::{telemetry::AssistantKind, ClickhouseEvent, TelemetrySettings}; diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index b6ef6b5cfa..66d2f60690 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -1,5 +1,6 @@ use crate::streaming_diff::{Hunk, StreamingDiff}; -use ai::completion::{CompletionProvider, OpenAIRequest}; +use ai::completion::CompletionProvider; +use ai::providers::open_ai::OpenAIRequest; use anyhow::Result; use editor::{multi_buffer, Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint}; use futures::{channel::mpsc, SinkExt, Stream, StreamExt}; From d813ae88458ed5e14899c5ebdd4437daa033ae6e Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 14:33:19 +0200 Subject: [PATCH 05/54] replace OpenAIRequest with more generalized Box --- crates/ai/src/completion.rs | 6 +++-- crates/ai/src/providers/dummy.rs | 13 ++++++++++ crates/ai/src/providers/mod.rs | 1 + crates/ai/src/providers/open_ai/completion.rs | 16 +++++++----- crates/assistant/src/assistant_panel.rs | 20 +++++++++------ crates/assistant/src/codegen.rs | 25 +++++++++++++------ 6 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 crates/ai/src/providers/dummy.rs diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index f45893898f..ba89c869d2 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -1,11 +1,13 @@ use anyhow::Result; use futures::{future::BoxFuture, stream::BoxStream}; -use crate::providers::open_ai::completion::OpenAIRequest; +pub trait CompletionRequest: Send + Sync { + fn data(&self) -> serde_json::Result; +} pub trait CompletionProvider { fn complete( &self, - prompt: OpenAIRequest, + prompt: Box, ) -> BoxFuture<'static, Result>>>; } diff --git a/crates/ai/src/providers/dummy.rs b/crates/ai/src/providers/dummy.rs new file mode 100644 index 0000000000..be42b13f2f --- /dev/null +++ b/crates/ai/src/providers/dummy.rs @@ -0,0 +1,13 @@ +use crate::completion::CompletionRequest; +use serde::Serialize; + +#[derive(Serialize)] +pub struct DummyCompletionRequest { + pub name: String, +} + +impl CompletionRequest for DummyCompletionRequest { + fn data(&self) -> serde_json::Result { + serde_json::to_string(self) + } +} diff --git a/crates/ai/src/providers/mod.rs b/crates/ai/src/providers/mod.rs index acd0f9d910..7a7092baf3 100644 --- a/crates/ai/src/providers/mod.rs +++ b/crates/ai/src/providers/mod.rs @@ -1 +1,2 @@ +pub mod dummy; pub mod open_ai; diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs index bb6138eee3..95ed13c0dd 100644 --- a/crates/ai/src/providers/open_ai/completion.rs +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -12,7 +12,7 @@ use std::{ sync::Arc, }; -use crate::completion::CompletionProvider; +use crate::completion::{CompletionProvider, CompletionRequest}; pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; @@ -59,6 +59,12 @@ pub struct OpenAIRequest { pub temperature: f32, } +impl CompletionRequest for OpenAIRequest { + fn data(&self) -> serde_json::Result { + serde_json::to_string(self) + } +} + #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct ResponseMessage { pub role: Option, @@ -92,13 +98,11 @@ pub struct OpenAIResponseStreamEvent { pub async fn stream_completion( api_key: String, executor: Arc, - mut request: OpenAIRequest, + request: Box, ) -> Result>> { - request.stream = true; - let (tx, rx) = futures::channel::mpsc::unbounded::>(); - let json_data = serde_json::to_string(&request)?; + let json_data = request.data()?; let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions")) .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", api_key)) @@ -189,7 +193,7 @@ impl OpenAICompletionProvider { impl CompletionProvider for OpenAICompletionProvider { fn complete( &self, - prompt: OpenAIRequest, + prompt: Box, ) -> BoxFuture<'static, Result>>> { let request = stream_completion(self.api_key.clone(), self.executor.clone(), prompt); async move { diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 9b749e5091..ec16c8fd04 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -6,8 +6,11 @@ use crate::{ SavedMessage, }; -use ai::providers::open_ai::{ - stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, +use ai::{ + completion::CompletionRequest, + providers::open_ai::{ + stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, + }, }; use ai::prompts::repository_context::PromptCodeSnippet; @@ -745,13 +748,14 @@ impl AssistantPanel { content: prompt, }); - let request = OpenAIRequest { + let request = Box::new(OpenAIRequest { model: model.full_name().into(), messages, stream: true, stop: vec!["|END|>".to_string()], temperature, - }; + }); + codegen.update(&mut cx, |codegen, cx| codegen.start(request, cx)); anyhow::Ok(()) }) @@ -1735,7 +1739,7 @@ impl Conversation { return Default::default(); }; - let request = OpenAIRequest { + let request: Box = Box::new(OpenAIRequest { model: self.model.full_name().to_string(), messages: self .messages(cx) @@ -1745,7 +1749,7 @@ impl Conversation { stream: true, stop: vec![], temperature: 1.0, - }; + }); let stream = stream_completion(api_key, cx.background().clone(), request); let assistant_message = self @@ -2025,13 +2029,13 @@ impl Conversation { "Summarize the conversation into a short title without punctuation" .into(), })); - let request = OpenAIRequest { + let request: Box = Box::new(OpenAIRequest { model: self.model.full_name().to_string(), messages: messages.collect(), stream: true, stop: vec![], temperature: 1.0, - }; + }); let stream = stream_completion(api_key, cx.background().clone(), request); self.pending_summary = cx.spawn(|this, mut cx| { diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index 66d2f60690..e535eca144 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -1,6 +1,5 @@ use crate::streaming_diff::{Hunk, StreamingDiff}; -use ai::completion::CompletionProvider; -use ai::providers::open_ai::OpenAIRequest; +use ai::completion::{CompletionProvider, CompletionRequest}; use anyhow::Result; use editor::{multi_buffer, Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint}; use futures::{channel::mpsc, SinkExt, Stream, StreamExt}; @@ -96,7 +95,7 @@ impl Codegen { self.error.as_ref() } - pub fn start(&mut self, prompt: OpenAIRequest, cx: &mut ModelContext) { + pub fn start(&mut self, prompt: Box, cx: &mut ModelContext) { let range = self.range(); let snapshot = self.snapshot.clone(); let selected_text = snapshot @@ -336,6 +335,7 @@ fn strip_markdown_codeblock( #[cfg(test)] mod tests { use super::*; + use ai::providers::dummy::DummyCompletionRequest; use futures::{ future::BoxFuture, stream::{self, BoxStream}, @@ -381,7 +381,10 @@ mod tests { cx, ) }); - codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); + let request = Box::new(DummyCompletionRequest { + name: "test".to_string(), + }); + codegen.update(cx, |codegen, cx| codegen.start(request, cx)); let mut new_text = concat!( " let mut x = 0;\n", @@ -443,7 +446,11 @@ mod tests { cx, ) }); - codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); + + let request = Box::new(DummyCompletionRequest { + name: "test".to_string(), + }); + codegen.update(cx, |codegen, cx| codegen.start(request, cx)); let mut new_text = concat!( "t mut x = 0;\n", @@ -505,7 +512,11 @@ mod tests { cx, ) }); - codegen.update(cx, |codegen, cx| codegen.start(Default::default(), cx)); + + let request = Box::new(DummyCompletionRequest { + name: "test".to_string(), + }); + codegen.update(cx, |codegen, cx| codegen.start(request, cx)); let mut new_text = concat!( "let mut x = 0;\n", @@ -617,7 +628,7 @@ mod tests { impl CompletionProvider for TestCompletionProvider { fn complete( &self, - _prompt: OpenAIRequest, + _prompt: Box, ) -> BoxFuture<'static, Result>>> { let (tx, rx) = mpsc::channel(1); *self.last_completion_tx.lock() = Some(tx); From d1dec8314adb8be628912642efeffb572ef83b71 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 14:46:22 +0200 Subject: [PATCH 06/54] move OpenAIEmbeddings to OpenAIEmbeddingProvider in providers folder --- crates/ai/src/embedding.rs | 287 +----------------- crates/ai/src/providers/dummy.rs | 37 ++- crates/ai/src/providers/open_ai/embedding.rs | 252 +++++++++++++++ crates/ai/src/providers/open_ai/mod.rs | 3 + crates/semantic_index/src/semantic_index.rs | 5 +- .../src/semantic_index_tests.rs | 19 +- crates/zed/examples/semantic_index_eval.rs | 4 +- 7 files changed, 308 insertions(+), 299 deletions(-) create mode 100644 crates/ai/src/providers/open_ai/embedding.rs diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index 4587ece0a2..05798c3f5d 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -1,30 +1,9 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use async_trait::async_trait; -use futures::AsyncReadExt; -use gpui::executor::Background; -use gpui::serde_json; -use isahc::http::StatusCode; -use isahc::prelude::Configurable; -use isahc::{AsyncBody, Response}; -use lazy_static::lazy_static; use ordered_float::OrderedFloat; -use parking_lot::Mutex; -use parse_duration::parse; -use postage::watch; use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::ToSql; -use serde::{Deserialize, Serialize}; -use std::env; -use std::ops::Add; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tiktoken_rs::{cl100k_base, CoreBPE}; -use util::http::{HttpClient, Request}; - -lazy_static! { - static ref OPENAI_API_KEY: Option = env::var("OPENAI_API_KEY").ok(); - static ref OPENAI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap(); -} +use std::time::Instant; #[derive(Debug, PartialEq, Clone)] pub struct Embedding(pub Vec); @@ -85,39 +64,6 @@ impl Embedding { } } -#[derive(Clone)] -pub struct OpenAIEmbeddings { - pub client: Arc, - pub executor: Arc, - rate_limit_count_rx: watch::Receiver>, - rate_limit_count_tx: Arc>>>, -} - -#[derive(Serialize)] -struct OpenAIEmbeddingRequest<'a> { - model: &'static str, - input: Vec<&'a str>, -} - -#[derive(Deserialize)] -struct OpenAIEmbeddingResponse { - data: Vec, - usage: OpenAIEmbeddingUsage, -} - -#[derive(Debug, Deserialize)] -struct OpenAIEmbedding { - embedding: Vec, - index: usize, - object: String, -} - -#[derive(Deserialize)] -struct OpenAIEmbeddingUsage { - prompt_tokens: usize, - total_tokens: usize, -} - #[async_trait] pub trait EmbeddingProvider: Sync + Send { fn is_authenticated(&self) -> bool; @@ -127,235 +73,6 @@ pub trait EmbeddingProvider: Sync + Send { fn rate_limit_expiration(&self) -> Option; } -pub struct DummyEmbeddings {} - -#[async_trait] -impl EmbeddingProvider for DummyEmbeddings { - fn is_authenticated(&self) -> bool { - true - } - fn rate_limit_expiration(&self) -> Option { - None - } - async fn embed_batch(&self, spans: Vec) -> Result> { - // 1024 is the OpenAI Embeddings size for ada models. - // the model we will likely be starting with. - let dummy_vec = Embedding::from(vec![0.32 as f32; 1536]); - return Ok(vec![dummy_vec; spans.len()]); - } - - fn max_tokens_per_batch(&self) -> usize { - OPENAI_INPUT_LIMIT - } - - fn truncate(&self, span: &str) -> (String, usize) { - let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); - let token_count = tokens.len(); - let output = if token_count > OPENAI_INPUT_LIMIT { - tokens.truncate(OPENAI_INPUT_LIMIT); - let new_input = OPENAI_BPE_TOKENIZER.decode(tokens.clone()); - new_input.ok().unwrap_or_else(|| span.to_string()) - } else { - span.to_string() - }; - - (output, tokens.len()) - } -} - -const OPENAI_INPUT_LIMIT: usize = 8190; - -impl OpenAIEmbeddings { - pub fn new(client: Arc, executor: Arc) -> Self { - let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None); - let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx)); - - OpenAIEmbeddings { - client, - executor, - rate_limit_count_rx, - rate_limit_count_tx, - } - } - - fn resolve_rate_limit(&self) { - let reset_time = *self.rate_limit_count_tx.lock().borrow(); - - if let Some(reset_time) = reset_time { - if Instant::now() >= reset_time { - *self.rate_limit_count_tx.lock().borrow_mut() = None - } - } - - log::trace!( - "resolving reset time: {:?}", - *self.rate_limit_count_tx.lock().borrow() - ); - } - - fn update_reset_time(&self, reset_time: Instant) { - let original_time = *self.rate_limit_count_tx.lock().borrow(); - - let updated_time = if let Some(original_time) = original_time { - if reset_time < original_time { - Some(reset_time) - } else { - Some(original_time) - } - } else { - Some(reset_time) - }; - - log::trace!("updating rate limit time: {:?}", updated_time); - - *self.rate_limit_count_tx.lock().borrow_mut() = updated_time; - } - async fn send_request( - &self, - api_key: &str, - spans: Vec<&str>, - request_timeout: u64, - ) -> Result> { - let request = Request::post("https://api.openai.com/v1/embeddings") - .redirect_policy(isahc::config::RedirectPolicy::Follow) - .timeout(Duration::from_secs(request_timeout)) - .header("Content-Type", "application/json") - .header("Authorization", format!("Bearer {}", api_key)) - .body( - serde_json::to_string(&OpenAIEmbeddingRequest { - input: spans.clone(), - model: "text-embedding-ada-002", - }) - .unwrap() - .into(), - )?; - - Ok(self.client.send(request).await?) - } -} - -#[async_trait] -impl EmbeddingProvider for OpenAIEmbeddings { - fn is_authenticated(&self) -> bool { - OPENAI_API_KEY.as_ref().is_some() - } - fn max_tokens_per_batch(&self) -> usize { - 50000 - } - - fn rate_limit_expiration(&self) -> Option { - *self.rate_limit_count_rx.borrow() - } - fn truncate(&self, span: &str) -> (String, usize) { - let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); - let output = if tokens.len() > OPENAI_INPUT_LIMIT { - tokens.truncate(OPENAI_INPUT_LIMIT); - OPENAI_BPE_TOKENIZER - .decode(tokens.clone()) - .ok() - .unwrap_or_else(|| span.to_string()) - } else { - span.to_string() - }; - - (output, tokens.len()) - } - - async fn embed_batch(&self, spans: Vec) -> Result> { - const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; - const MAX_RETRIES: usize = 4; - - let api_key = OPENAI_API_KEY - .as_ref() - .ok_or_else(|| anyhow!("no api key"))?; - - let mut request_number = 0; - let mut rate_limiting = false; - let mut request_timeout: u64 = 15; - let mut response: Response; - while request_number < MAX_RETRIES { - response = self - .send_request( - api_key, - spans.iter().map(|x| &**x).collect(), - request_timeout, - ) - .await?; - - request_number += 1; - - match response.status() { - StatusCode::REQUEST_TIMEOUT => { - request_timeout += 5; - } - StatusCode::OK => { - let mut body = String::new(); - response.body_mut().read_to_string(&mut body).await?; - let response: OpenAIEmbeddingResponse = serde_json::from_str(&body)?; - - log::trace!( - "openai embedding completed. tokens: {:?}", - response.usage.total_tokens - ); - - // If we complete a request successfully that was previously rate_limited - // resolve the rate limit - if rate_limiting { - self.resolve_rate_limit() - } - - return Ok(response - .data - .into_iter() - .map(|embedding| Embedding::from(embedding.embedding)) - .collect()); - } - StatusCode::TOO_MANY_REQUESTS => { - rate_limiting = true; - let mut body = String::new(); - response.body_mut().read_to_string(&mut body).await?; - - let delay_duration = { - let delay = Duration::from_secs(BACKOFF_SECONDS[request_number - 1] as u64); - if let Some(time_to_reset) = - response.headers().get("x-ratelimit-reset-tokens") - { - if let Ok(time_str) = time_to_reset.to_str() { - parse(time_str).unwrap_or(delay) - } else { - delay - } - } else { - delay - } - }; - - // If we've previously rate limited, increment the duration but not the count - let reset_time = Instant::now().add(delay_duration); - self.update_reset_time(reset_time); - - log::trace!( - "openai rate limiting: waiting {:?} until lifted", - &delay_duration - ); - - self.executor.timer(delay_duration).await; - } - _ => { - let mut body = String::new(); - response.body_mut().read_to_string(&mut body).await?; - return Err(anyhow!( - "open ai bad request: {:?} {:?}", - &response.status(), - body - )); - } - } - } - Err(anyhow!("openai max retries")) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/ai/src/providers/dummy.rs b/crates/ai/src/providers/dummy.rs index be42b13f2f..8061a2ca6b 100644 --- a/crates/ai/src/providers/dummy.rs +++ b/crates/ai/src/providers/dummy.rs @@ -1,4 +1,10 @@ -use crate::completion::CompletionRequest; +use std::time::Instant; + +use crate::{ + completion::CompletionRequest, + embedding::{Embedding, EmbeddingProvider}, +}; +use async_trait::async_trait; use serde::Serialize; #[derive(Serialize)] @@ -11,3 +17,32 @@ impl CompletionRequest for DummyCompletionRequest { serde_json::to_string(self) } } + +pub struct DummyEmbeddingProvider {} + +#[async_trait] +impl EmbeddingProvider for DummyEmbeddingProvider { + fn is_authenticated(&self) -> bool { + true + } + fn rate_limit_expiration(&self) -> Option { + None + } + async fn embed_batch(&self, spans: Vec) -> anyhow::Result> { + // 1024 is the OpenAI Embeddings size for ada models. + // the model we will likely be starting with. + let dummy_vec = Embedding::from(vec![0.32 as f32; 1536]); + return Ok(vec![dummy_vec; spans.len()]); + } + + fn max_tokens_per_batch(&self) -> usize { + 8190 + } + + fn truncate(&self, span: &str) -> (String, usize) { + let truncated = span.chars().collect::>()[..8190] + .iter() + .collect::(); + (truncated, 8190) + } +} diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs new file mode 100644 index 0000000000..35398394dc --- /dev/null +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -0,0 +1,252 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures::AsyncReadExt; +use gpui::executor::Background; +use gpui::serde_json; +use isahc::http::StatusCode; +use isahc::prelude::Configurable; +use isahc::{AsyncBody, Response}; +use lazy_static::lazy_static; +use parking_lot::Mutex; +use parse_duration::parse; +use postage::watch; +use serde::{Deserialize, Serialize}; +use std::env; +use std::ops::Add; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tiktoken_rs::{cl100k_base, CoreBPE}; +use util::http::{HttpClient, Request}; + +use crate::embedding::{Embedding, EmbeddingProvider}; + +lazy_static! { + static ref OPENAI_API_KEY: Option = env::var("OPENAI_API_KEY").ok(); + static ref OPENAI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap(); +} + +#[derive(Clone)] +pub struct OpenAIEmbeddingProvider { + pub client: Arc, + pub executor: Arc, + rate_limit_count_rx: watch::Receiver>, + rate_limit_count_tx: Arc>>>, +} + +#[derive(Serialize)] +struct OpenAIEmbeddingRequest<'a> { + model: &'static str, + input: Vec<&'a str>, +} + +#[derive(Deserialize)] +struct OpenAIEmbeddingResponse { + data: Vec, + usage: OpenAIEmbeddingUsage, +} + +#[derive(Debug, Deserialize)] +struct OpenAIEmbedding { + embedding: Vec, + index: usize, + object: String, +} + +#[derive(Deserialize)] +struct OpenAIEmbeddingUsage { + prompt_tokens: usize, + total_tokens: usize, +} + +const OPENAI_INPUT_LIMIT: usize = 8190; + +impl OpenAIEmbeddingProvider { + pub fn new(client: Arc, executor: Arc) -> Self { + let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None); + let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx)); + + OpenAIEmbeddingProvider { + client, + executor, + rate_limit_count_rx, + rate_limit_count_tx, + } + } + + fn resolve_rate_limit(&self) { + let reset_time = *self.rate_limit_count_tx.lock().borrow(); + + if let Some(reset_time) = reset_time { + if Instant::now() >= reset_time { + *self.rate_limit_count_tx.lock().borrow_mut() = None + } + } + + log::trace!( + "resolving reset time: {:?}", + *self.rate_limit_count_tx.lock().borrow() + ); + } + + fn update_reset_time(&self, reset_time: Instant) { + let original_time = *self.rate_limit_count_tx.lock().borrow(); + + let updated_time = if let Some(original_time) = original_time { + if reset_time < original_time { + Some(reset_time) + } else { + Some(original_time) + } + } else { + Some(reset_time) + }; + + log::trace!("updating rate limit time: {:?}", updated_time); + + *self.rate_limit_count_tx.lock().borrow_mut() = updated_time; + } + async fn send_request( + &self, + api_key: &str, + spans: Vec<&str>, + request_timeout: u64, + ) -> Result> { + let request = Request::post("https://api.openai.com/v1/embeddings") + .redirect_policy(isahc::config::RedirectPolicy::Follow) + .timeout(Duration::from_secs(request_timeout)) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", api_key)) + .body( + serde_json::to_string(&OpenAIEmbeddingRequest { + input: spans.clone(), + model: "text-embedding-ada-002", + }) + .unwrap() + .into(), + )?; + + Ok(self.client.send(request).await?) + } +} + +#[async_trait] +impl EmbeddingProvider for OpenAIEmbeddingProvider { + fn is_authenticated(&self) -> bool { + OPENAI_API_KEY.as_ref().is_some() + } + fn max_tokens_per_batch(&self) -> usize { + 50000 + } + + fn rate_limit_expiration(&self) -> Option { + *self.rate_limit_count_rx.borrow() + } + fn truncate(&self, span: &str) -> (String, usize) { + let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); + let output = if tokens.len() > OPENAI_INPUT_LIMIT { + tokens.truncate(OPENAI_INPUT_LIMIT); + OPENAI_BPE_TOKENIZER + .decode(tokens.clone()) + .ok() + .unwrap_or_else(|| span.to_string()) + } else { + span.to_string() + }; + + (output, tokens.len()) + } + + async fn embed_batch(&self, spans: Vec) -> Result> { + const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; + const MAX_RETRIES: usize = 4; + + let api_key = OPENAI_API_KEY + .as_ref() + .ok_or_else(|| anyhow!("no api key"))?; + + let mut request_number = 0; + let mut rate_limiting = false; + let mut request_timeout: u64 = 15; + let mut response: Response; + while request_number < MAX_RETRIES { + response = self + .send_request( + api_key, + spans.iter().map(|x| &**x).collect(), + request_timeout, + ) + .await?; + + request_number += 1; + + match response.status() { + StatusCode::REQUEST_TIMEOUT => { + request_timeout += 5; + } + StatusCode::OK => { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + let response: OpenAIEmbeddingResponse = serde_json::from_str(&body)?; + + log::trace!( + "openai embedding completed. tokens: {:?}", + response.usage.total_tokens + ); + + // If we complete a request successfully that was previously rate_limited + // resolve the rate limit + if rate_limiting { + self.resolve_rate_limit() + } + + return Ok(response + .data + .into_iter() + .map(|embedding| Embedding::from(embedding.embedding)) + .collect()); + } + StatusCode::TOO_MANY_REQUESTS => { + rate_limiting = true; + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + let delay_duration = { + let delay = Duration::from_secs(BACKOFF_SECONDS[request_number - 1] as u64); + if let Some(time_to_reset) = + response.headers().get("x-ratelimit-reset-tokens") + { + if let Ok(time_str) = time_to_reset.to_str() { + parse(time_str).unwrap_or(delay) + } else { + delay + } + } else { + delay + } + }; + + // If we've previously rate limited, increment the duration but not the count + let reset_time = Instant::now().add(delay_duration); + self.update_reset_time(reset_time); + + log::trace!( + "openai rate limiting: waiting {:?} until lifted", + &delay_duration + ); + + self.executor.timer(delay_duration).await; + } + _ => { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + return Err(anyhow!( + "open ai bad request: {:?} {:?}", + &response.status(), + body + )); + } + } + } + Err(anyhow!("openai max retries")) + } +} diff --git a/crates/ai/src/providers/open_ai/mod.rs b/crates/ai/src/providers/open_ai/mod.rs index 26f3068ca1..67cb2b5315 100644 --- a/crates/ai/src/providers/open_ai/mod.rs +++ b/crates/ai/src/providers/open_ai/mod.rs @@ -1,4 +1,7 @@ pub mod completion; +pub mod embedding; pub mod model; + pub use completion::*; +pub use embedding::*; pub use model::OpenAILanguageModel; diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index ecdba43643..926eb3045c 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -7,7 +7,8 @@ pub mod semantic_index_settings; mod semantic_index_tests; use crate::semantic_index_settings::SemanticIndexSettings; -use ai::embedding::{Embedding, EmbeddingProvider, OpenAIEmbeddings}; +use ai::embedding::{Embedding, EmbeddingProvider}; +use ai::providers::open_ai::OpenAIEmbeddingProvider; use anyhow::{anyhow, Result}; use collections::{BTreeMap, HashMap, HashSet}; use db::VectorDatabase; @@ -88,7 +89,7 @@ pub fn init( let semantic_index = SemanticIndex::new( fs, db_file_path, - Arc::new(OpenAIEmbeddings::new(http_client, cx.background())), + Arc::new(OpenAIEmbeddingProvider::new(http_client, cx.background())), language_registry, cx.clone(), ) diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 182010ca83..6842ce5c5d 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -4,7 +4,8 @@ use crate::{ semantic_index_settings::SemanticIndexSettings, FileToEmbed, JobHandle, SearchResult, SemanticIndex, EMBEDDING_QUEUE_FLUSH_TIMEOUT, }; -use ai::embedding::{DummyEmbeddings, Embedding, EmbeddingProvider}; +use ai::embedding::{Embedding, EmbeddingProvider}; +use ai::providers::dummy::DummyEmbeddingProvider; use anyhow::Result; use async_trait::async_trait; use gpui::{executor::Deterministic, Task, TestAppContext}; @@ -280,7 +281,7 @@ fn assert_search_results( #[gpui::test] async fn test_code_context_retrieval_rust() { let language = rust_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = " @@ -382,7 +383,7 @@ async fn test_code_context_retrieval_rust() { #[gpui::test] async fn test_code_context_retrieval_json() { let language = json_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -466,7 +467,7 @@ fn assert_documents_eq( #[gpui::test] async fn test_code_context_retrieval_javascript() { let language = js_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = " @@ -565,7 +566,7 @@ async fn test_code_context_retrieval_javascript() { #[gpui::test] async fn test_code_context_retrieval_lua() { let language = lua_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -639,7 +640,7 @@ async fn test_code_context_retrieval_lua() { #[gpui::test] async fn test_code_context_retrieval_elixir() { let language = elixir_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -756,7 +757,7 @@ async fn test_code_context_retrieval_elixir() { #[gpui::test] async fn test_code_context_retrieval_cpp() { let language = cpp_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = " @@ -909,7 +910,7 @@ async fn test_code_context_retrieval_cpp() { #[gpui::test] async fn test_code_context_retrieval_ruby() { let language = ruby_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -1100,7 +1101,7 @@ async fn test_code_context_retrieval_ruby() { #[gpui::test] async fn test_code_context_retrieval_php() { let language = php_lang(); - let embedding_provider = Arc::new(DummyEmbeddings {}); + let embedding_provider = Arc::new(DummyEmbeddingProvider {}); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" diff --git a/crates/zed/examples/semantic_index_eval.rs b/crates/zed/examples/semantic_index_eval.rs index 33d6b3689c..0bada47502 100644 --- a/crates/zed/examples/semantic_index_eval.rs +++ b/crates/zed/examples/semantic_index_eval.rs @@ -1,4 +1,4 @@ -use ai::embedding::OpenAIEmbeddings; +use ai::providers::open_ai::OpenAIEmbeddingProvider; use anyhow::{anyhow, Result}; use client::{self, UserStore}; use gpui::{AsyncAppContext, ModelHandle, Task}; @@ -474,7 +474,7 @@ fn main() { let semantic_index = SemanticIndex::new( fs.clone(), db_file_path, - Arc::new(OpenAIEmbeddings::new(http_client, cx.background())), + Arc::new(OpenAIEmbeddingProvider::new(http_client, cx.background())), languages.clone(), cx.clone(), ) From 2b780ee7b2e63d990400c19c002eb1fc2f7bdfd7 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sun, 22 Oct 2023 15:00:09 +0200 Subject: [PATCH 07/54] add base model to EmbeddingProvider, not yet leveraged for truncation --- crates/ai/src/embedding.rs | 3 ++ crates/ai/src/providers/dummy.rs | 35 +++++++++++++++++++ crates/ai/src/providers/open_ai/embedding.rs | 10 ++++++ crates/ai/src/providers/open_ai/model.rs | 1 + .../src/semantic_index_tests.rs | 10 ++++-- 5 files changed, 57 insertions(+), 2 deletions(-) diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index 05798c3f5d..f792406c8b 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -5,6 +5,8 @@ use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::ToSql; use std::time::Instant; +use crate::models::LanguageModel; + #[derive(Debug, PartialEq, Clone)] pub struct Embedding(pub Vec); @@ -66,6 +68,7 @@ impl Embedding { #[async_trait] pub trait EmbeddingProvider: Sync + Send { + fn base_model(&self) -> Box; fn is_authenticated(&self) -> bool; async fn embed_batch(&self, spans: Vec) -> Result>; fn max_tokens_per_batch(&self) -> usize; diff --git a/crates/ai/src/providers/dummy.rs b/crates/ai/src/providers/dummy.rs index 8061a2ca6b..9df5547da1 100644 --- a/crates/ai/src/providers/dummy.rs +++ b/crates/ai/src/providers/dummy.rs @@ -3,10 +3,42 @@ use std::time::Instant; use crate::{ completion::CompletionRequest, embedding::{Embedding, EmbeddingProvider}, + models::{LanguageModel, TruncationDirection}, }; use async_trait::async_trait; use serde::Serialize; +pub struct DummyLanguageModel {} + +impl LanguageModel for DummyLanguageModel { + fn name(&self) -> String { + "dummy".to_string() + } + fn capacity(&self) -> anyhow::Result { + anyhow::Ok(1000) + } + fn truncate( + &self, + content: &str, + length: usize, + direction: crate::models::TruncationDirection, + ) -> anyhow::Result { + let truncated = match direction { + TruncationDirection::End => content.chars().collect::>()[..length] + .iter() + .collect::(), + TruncationDirection::Start => content.chars().collect::>()[..length] + .iter() + .collect::(), + }; + + anyhow::Ok(truncated) + } + fn count_tokens(&self, content: &str) -> anyhow::Result { + anyhow::Ok(content.chars().collect::>().len()) + } +} + #[derive(Serialize)] pub struct DummyCompletionRequest { pub name: String, @@ -22,6 +54,9 @@ pub struct DummyEmbeddingProvider {} #[async_trait] impl EmbeddingProvider for DummyEmbeddingProvider { + fn base_model(&self) -> Box { + Box::new(DummyLanguageModel {}) + } fn is_authenticated(&self) -> bool { true } diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index 35398394dc..ed028177f6 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -19,6 +19,8 @@ use tiktoken_rs::{cl100k_base, CoreBPE}; use util::http::{HttpClient, Request}; use crate::embedding::{Embedding, EmbeddingProvider}; +use crate::models::LanguageModel; +use crate::providers::open_ai::OpenAILanguageModel; lazy_static! { static ref OPENAI_API_KEY: Option = env::var("OPENAI_API_KEY").ok(); @@ -27,6 +29,7 @@ lazy_static! { #[derive(Clone)] pub struct OpenAIEmbeddingProvider { + model: OpenAILanguageModel, pub client: Arc, pub executor: Arc, rate_limit_count_rx: watch::Receiver>, @@ -65,7 +68,10 @@ impl OpenAIEmbeddingProvider { let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None); let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx)); + let model = OpenAILanguageModel::load("text-embedding-ada-002"); + OpenAIEmbeddingProvider { + model, client, executor, rate_limit_count_rx, @@ -131,6 +137,10 @@ impl OpenAIEmbeddingProvider { #[async_trait] impl EmbeddingProvider for OpenAIEmbeddingProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(self.model.clone()); + model + } fn is_authenticated(&self) -> bool { OPENAI_API_KEY.as_ref().is_some() } diff --git a/crates/ai/src/providers/open_ai/model.rs b/crates/ai/src/providers/open_ai/model.rs index 42523f3df4..6e306c80b9 100644 --- a/crates/ai/src/providers/open_ai/model.rs +++ b/crates/ai/src/providers/open_ai/model.rs @@ -4,6 +4,7 @@ use util::ResultExt; use crate::models::{LanguageModel, TruncationDirection}; +#[derive(Clone)] pub struct OpenAILanguageModel { name: String, bpe: Option, diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 6842ce5c5d..43779f5b6c 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -4,8 +4,11 @@ use crate::{ semantic_index_settings::SemanticIndexSettings, FileToEmbed, JobHandle, SearchResult, SemanticIndex, EMBEDDING_QUEUE_FLUSH_TIMEOUT, }; -use ai::embedding::{Embedding, EmbeddingProvider}; -use ai::providers::dummy::DummyEmbeddingProvider; +use ai::providers::dummy::{DummyEmbeddingProvider, DummyLanguageModel}; +use ai::{ + embedding::{Embedding, EmbeddingProvider}, + models::LanguageModel, +}; use anyhow::Result; use async_trait::async_trait; use gpui::{executor::Deterministic, Task, TestAppContext}; @@ -1282,6 +1285,9 @@ impl FakeEmbeddingProvider { #[async_trait] impl EmbeddingProvider for FakeEmbeddingProvider { + fn base_model(&self) -> Box { + Box::new(DummyLanguageModel {}) + } fn is_authenticated(&self) -> bool { true } From 4e90e4599973b2016370b99a5406bb1a49ca21f4 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 23 Oct 2023 14:07:45 +0200 Subject: [PATCH 08/54] move embedding truncation to base model --- crates/ai/src/embedding.rs | 1 - crates/ai/src/providers/dummy.rs | 11 +++---- crates/ai/src/providers/open_ai/embedding.rs | 28 ++++++++-------- crates/semantic_index/src/parsing.rs | 33 ++++++++++++++++--- .../src/semantic_index_tests.rs | 9 ++--- 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index f792406c8b..4e67f44cae 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -72,7 +72,6 @@ pub trait EmbeddingProvider: Sync + Send { fn is_authenticated(&self) -> bool; async fn embed_batch(&self, spans: Vec) -> Result>; fn max_tokens_per_batch(&self) -> usize; - fn truncate(&self, span: &str) -> (String, usize); fn rate_limit_expiration(&self) -> Option; } diff --git a/crates/ai/src/providers/dummy.rs b/crates/ai/src/providers/dummy.rs index 9df5547da1..7eef16111d 100644 --- a/crates/ai/src/providers/dummy.rs +++ b/crates/ai/src/providers/dummy.rs @@ -23,6 +23,10 @@ impl LanguageModel for DummyLanguageModel { length: usize, direction: crate::models::TruncationDirection, ) -> anyhow::Result { + if content.len() < length { + return anyhow::Ok(content.to_string()); + } + let truncated = match direction { TruncationDirection::End => content.chars().collect::>()[..length] .iter() @@ -73,11 +77,4 @@ impl EmbeddingProvider for DummyEmbeddingProvider { fn max_tokens_per_batch(&self) -> usize { 8190 } - - fn truncate(&self, span: &str) -> (String, usize) { - let truncated = span.chars().collect::>()[..8190] - .iter() - .collect::(); - (truncated, 8190) - } } diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index ed028177f6..3689cb36f4 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -61,8 +61,6 @@ struct OpenAIEmbeddingUsage { total_tokens: usize, } -const OPENAI_INPUT_LIMIT: usize = 8190; - impl OpenAIEmbeddingProvider { pub fn new(client: Arc, executor: Arc) -> Self { let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None); @@ -151,20 +149,20 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider { fn rate_limit_expiration(&self) -> Option { *self.rate_limit_count_rx.borrow() } - fn truncate(&self, span: &str) -> (String, usize) { - let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); - let output = if tokens.len() > OPENAI_INPUT_LIMIT { - tokens.truncate(OPENAI_INPUT_LIMIT); - OPENAI_BPE_TOKENIZER - .decode(tokens.clone()) - .ok() - .unwrap_or_else(|| span.to_string()) - } else { - span.to_string() - }; + // fn truncate(&self, span: &str) -> (String, usize) { + // let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); + // let output = if tokens.len() > OPENAI_INPUT_LIMIT { + // tokens.truncate(OPENAI_INPUT_LIMIT); + // OPENAI_BPE_TOKENIZER + // .decode(tokens.clone()) + // .ok() + // .unwrap_or_else(|| span.to_string()) + // } else { + // span.to_string() + // }; - (output, tokens.len()) - } + // (output, tokens.len()) + // } async fn embed_batch(&self, spans: Vec) -> Result> { const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; diff --git a/crates/semantic_index/src/parsing.rs b/crates/semantic_index/src/parsing.rs index f9b8bac9a4..cb15ca453b 100644 --- a/crates/semantic_index/src/parsing.rs +++ b/crates/semantic_index/src/parsing.rs @@ -1,4 +1,7 @@ -use ai::embedding::{Embedding, EmbeddingProvider}; +use ai::{ + embedding::{Embedding, EmbeddingProvider}, + models::TruncationDirection, +}; use anyhow::{anyhow, Result}; use language::{Grammar, Language}; use rusqlite::{ @@ -108,7 +111,14 @@ impl CodeContextRetriever { .replace("", language_name.as_ref()) .replace("", &content); let digest = SpanDigest::from(document_span.as_str()); - let (document_span, token_count) = self.embedding_provider.truncate(&document_span); + let model = self.embedding_provider.base_model(); + let document_span = model.truncate( + &document_span, + model.capacity()?, + ai::models::TruncationDirection::End, + )?; + let token_count = model.count_tokens(&document_span)?; + Ok(vec![Span { range: 0..content.len(), content: document_span, @@ -131,7 +141,15 @@ impl CodeContextRetriever { ) .replace("", &content); let digest = SpanDigest::from(document_span.as_str()); - let (document_span, token_count) = self.embedding_provider.truncate(&document_span); + + let model = self.embedding_provider.base_model(); + let document_span = model.truncate( + &document_span, + model.capacity()?, + ai::models::TruncationDirection::End, + )?; + let token_count = model.count_tokens(&document_span)?; + Ok(vec![Span { range: 0..content.len(), content: document_span, @@ -222,8 +240,13 @@ impl CodeContextRetriever { .replace("", language_name.as_ref()) .replace("item", &span.content); - let (document_content, token_count) = - self.embedding_provider.truncate(&document_content); + let model = self.embedding_provider.base_model(); + let document_content = model.truncate( + &document_content, + model.capacity()?, + TruncationDirection::End, + )?; + let token_count = model.count_tokens(&document_content)?; span.content = document_content; span.token_count = token_count; diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 43779f5b6c..002dee33e3 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -1291,12 +1291,8 @@ impl EmbeddingProvider for FakeEmbeddingProvider { fn is_authenticated(&self) -> bool { true } - fn truncate(&self, span: &str) -> (String, usize) { - (span.to_string(), 1) - } - fn max_tokens_per_batch(&self) -> usize { - 200 + 1000 } fn rate_limit_expiration(&self) -> Option { @@ -1306,7 +1302,8 @@ impl EmbeddingProvider for FakeEmbeddingProvider { async fn embed_batch(&self, spans: Vec) -> Result> { self.embedding_count .fetch_add(spans.len(), atomic::Ordering::SeqCst); - Ok(spans.iter().map(|span| self.embed_sync(span)).collect()) + + anyhow::Ok(spans.iter().map(|span| self.embed_sync(span)).collect()) } } From 3447a9478c62476728f1e0131d708699dad2bcd1 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 26 Oct 2023 11:18:16 +0200 Subject: [PATCH 09/54] updated authentication for embedding provider --- crates/ai/Cargo.toml | 3 + crates/ai/src/ai.rs | 3 + crates/ai/src/auth.rs | 20 +++ crates/ai/src/embedding.rs | 8 +- crates/ai/src/prompts/base.rs | 41 +----- crates/ai/src/providers/dummy.rs | 85 ------------ crates/ai/src/providers/mod.rs | 1 - crates/ai/src/providers/open_ai/auth.rs | 33 +++++ crates/ai/src/providers/open_ai/embedding.rs | 46 ++----- crates/ai/src/providers/open_ai/mod.rs | 1 + crates/ai/src/test.rs | 123 ++++++++++++++++++ crates/assistant/src/codegen.rs | 14 +- crates/semantic_index/Cargo.toml | 1 + crates/semantic_index/src/embedding_queue.rs | 16 +-- crates/semantic_index/src/semantic_index.rs | 52 +++++--- .../src/semantic_index_tests.rs | 101 +++----------- 16 files changed, 277 insertions(+), 271 deletions(-) create mode 100644 crates/ai/src/auth.rs delete mode 100644 crates/ai/src/providers/dummy.rs create mode 100644 crates/ai/src/providers/open_ai/auth.rs create mode 100644 crates/ai/src/test.rs diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index b24c4e5ece..fb49a4b515 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -8,6 +8,9 @@ publish = false path = "src/ai.rs" doctest = false +[features] +test-support = [] + [dependencies] gpui = { path = "../gpui" } util = { path = "../util" } diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index a3ae2fcf7f..dda22d2a1d 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -1,5 +1,8 @@ +pub mod auth; pub mod completion; pub mod embedding; pub mod models; pub mod prompts; pub mod providers; +#[cfg(any(test, feature = "test-support"))] +pub mod test; diff --git a/crates/ai/src/auth.rs b/crates/ai/src/auth.rs new file mode 100644 index 0000000000..a3ce8aece1 --- /dev/null +++ b/crates/ai/src/auth.rs @@ -0,0 +1,20 @@ +use gpui::AppContext; + +#[derive(Clone)] +pub enum ProviderCredential { + Credentials { api_key: String }, + NoCredentials, + NotNeeded, +} + +pub trait CredentialProvider: Send + Sync { + fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential; +} + +#[derive(Clone)] +pub struct NullCredentialProvider; +impl CredentialProvider for NullCredentialProvider { + fn retrieve_credentials(&self, _cx: &AppContext) -> ProviderCredential { + ProviderCredential::NotNeeded + } +} diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index 8cfc901525..50f04232ab 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -7,6 +7,7 @@ use ordered_float::OrderedFloat; use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::ToSql; +use crate::auth::{CredentialProvider, ProviderCredential}; use crate::models::LanguageModel; #[derive(Debug, PartialEq, Clone)] @@ -71,11 +72,14 @@ impl Embedding { #[async_trait] pub trait EmbeddingProvider: Sync + Send { fn base_model(&self) -> Box; - fn retrieve_credentials(&self, cx: &AppContext) -> Option; + fn credential_provider(&self) -> Box; + fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { + self.credential_provider().retrieve_credentials(cx) + } async fn embed_batch( &self, spans: Vec, - api_key: Option, + credential: ProviderCredential, ) -> Result>; fn max_tokens_per_batch(&self) -> usize; fn rate_limit_expiration(&self) -> Option; diff --git a/crates/ai/src/prompts/base.rs b/crates/ai/src/prompts/base.rs index f0ff597e63..a2106c7410 100644 --- a/crates/ai/src/prompts/base.rs +++ b/crates/ai/src/prompts/base.rs @@ -126,6 +126,7 @@ impl PromptChain { #[cfg(test)] pub(crate) mod tests { use crate::models::TruncationDirection; + use crate::test::FakeLanguageModel; use super::*; @@ -181,39 +182,7 @@ pub(crate) mod tests { } } - #[derive(Clone)] - struct DummyLanguageModel { - capacity: usize, - } - - impl LanguageModel for DummyLanguageModel { - fn name(&self) -> String { - "dummy".to_string() - } - fn count_tokens(&self, content: &str) -> anyhow::Result { - anyhow::Ok(content.chars().collect::>().len()) - } - fn truncate( - &self, - content: &str, - length: usize, - direction: TruncationDirection, - ) -> anyhow::Result { - anyhow::Ok(match direction { - TruncationDirection::End => content.chars().collect::>()[..length] - .into_iter() - .collect::(), - TruncationDirection::Start => content.chars().collect::>()[length..] - .into_iter() - .collect::(), - }) - } - fn capacity(&self) -> anyhow::Result { - anyhow::Ok(self.capacity) - } - } - - let model: Arc = Arc::new(DummyLanguageModel { capacity: 100 }); + let model: Arc = Arc::new(FakeLanguageModel { capacity: 100 }); let args = PromptArguments { model: model.clone(), language_name: None, @@ -249,7 +218,7 @@ pub(crate) mod tests { // Testing with Truncation Off // Should ignore capacity and return all prompts - let model: Arc = Arc::new(DummyLanguageModel { capacity: 20 }); + let model: Arc = Arc::new(FakeLanguageModel { capacity: 20 }); let args = PromptArguments { model: model.clone(), language_name: None, @@ -286,7 +255,7 @@ pub(crate) mod tests { // Testing with Truncation Off // Should ignore capacity and return all prompts let capacity = 20; - let model: Arc = Arc::new(DummyLanguageModel { capacity }); + let model: Arc = Arc::new(FakeLanguageModel { capacity }); let args = PromptArguments { model: model.clone(), language_name: None, @@ -322,7 +291,7 @@ pub(crate) mod tests { // Change Ordering of Prompts Based on Priority let capacity = 120; let reserved_tokens = 10; - let model: Arc = Arc::new(DummyLanguageModel { capacity }); + let model: Arc = Arc::new(FakeLanguageModel { capacity }); let args = PromptArguments { model: model.clone(), language_name: None, diff --git a/crates/ai/src/providers/dummy.rs b/crates/ai/src/providers/dummy.rs deleted file mode 100644 index 2ee26488bd..0000000000 --- a/crates/ai/src/providers/dummy.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::time::Instant; - -use crate::{ - completion::CompletionRequest, - embedding::{Embedding, EmbeddingProvider}, - models::{LanguageModel, TruncationDirection}, -}; -use async_trait::async_trait; -use gpui::AppContext; -use serde::Serialize; - -pub struct DummyLanguageModel {} - -impl LanguageModel for DummyLanguageModel { - fn name(&self) -> String { - "dummy".to_string() - } - fn capacity(&self) -> anyhow::Result { - anyhow::Ok(1000) - } - fn truncate( - &self, - content: &str, - length: usize, - direction: crate::models::TruncationDirection, - ) -> anyhow::Result { - if content.len() < length { - return anyhow::Ok(content.to_string()); - } - - let truncated = match direction { - TruncationDirection::End => content.chars().collect::>()[..length] - .iter() - .collect::(), - TruncationDirection::Start => content.chars().collect::>()[..length] - .iter() - .collect::(), - }; - - anyhow::Ok(truncated) - } - fn count_tokens(&self, content: &str) -> anyhow::Result { - anyhow::Ok(content.chars().collect::>().len()) - } -} - -#[derive(Serialize)] -pub struct DummyCompletionRequest { - pub name: String, -} - -impl CompletionRequest for DummyCompletionRequest { - fn data(&self) -> serde_json::Result { - serde_json::to_string(self) - } -} - -pub struct DummyEmbeddingProvider {} - -#[async_trait] -impl EmbeddingProvider for DummyEmbeddingProvider { - fn retrieve_credentials(&self, _cx: &AppContext) -> Option { - Some("Dummy Credentials".to_string()) - } - fn base_model(&self) -> Box { - Box::new(DummyLanguageModel {}) - } - fn rate_limit_expiration(&self) -> Option { - None - } - async fn embed_batch( - &self, - spans: Vec, - api_key: Option, - ) -> anyhow::Result> { - // 1024 is the OpenAI Embeddings size for ada models. - // the model we will likely be starting with. - let dummy_vec = Embedding::from(vec![0.32 as f32; 1536]); - return Ok(vec![dummy_vec; spans.len()]); - } - - fn max_tokens_per_batch(&self) -> usize { - 8190 - } -} diff --git a/crates/ai/src/providers/mod.rs b/crates/ai/src/providers/mod.rs index 7a7092baf3..acd0f9d910 100644 --- a/crates/ai/src/providers/mod.rs +++ b/crates/ai/src/providers/mod.rs @@ -1,2 +1 @@ -pub mod dummy; pub mod open_ai; diff --git a/crates/ai/src/providers/open_ai/auth.rs b/crates/ai/src/providers/open_ai/auth.rs new file mode 100644 index 0000000000..c817ffea00 --- /dev/null +++ b/crates/ai/src/providers/open_ai/auth.rs @@ -0,0 +1,33 @@ +use std::env; + +use gpui::AppContext; +use util::ResultExt; + +use crate::auth::{CredentialProvider, ProviderCredential}; +use crate::providers::open_ai::OPENAI_API_URL; + +#[derive(Clone)] +pub struct OpenAICredentialProvider {} + +impl CredentialProvider for OpenAICredentialProvider { + fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { + let api_key = if let Ok(api_key) = env::var("OPENAI_API_KEY") { + Some(api_key) + } else if let Some((_, api_key)) = cx + .platform() + .read_credentials(OPENAI_API_URL) + .log_err() + .flatten() + { + String::from_utf8(api_key).log_err() + } else { + None + }; + + if let Some(api_key) = api_key { + ProviderCredential::Credentials { api_key } + } else { + ProviderCredential::NoCredentials + } + } +} diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index 805a906dda..1385b32b4d 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::AsyncReadExt; use gpui::executor::Background; -use gpui::{serde_json, AppContext}; +use gpui::serde_json; use isahc::http::StatusCode; use isahc::prelude::Configurable; use isahc::{AsyncBody, Response}; @@ -17,13 +17,13 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use tiktoken_rs::{cl100k_base, CoreBPE}; use util::http::{HttpClient, Request}; -use util::ResultExt; +use crate::auth::{CredentialProvider, ProviderCredential}; use crate::embedding::{Embedding, EmbeddingProvider}; use crate::models::LanguageModel; use crate::providers::open_ai::OpenAILanguageModel; -use super::OPENAI_API_URL; +use crate::providers::open_ai::auth::OpenAICredentialProvider; lazy_static! { static ref OPENAI_API_KEY: Option = env::var("OPENAI_API_KEY").ok(); @@ -33,6 +33,7 @@ lazy_static! { #[derive(Clone)] pub struct OpenAIEmbeddingProvider { model: OpenAILanguageModel, + credential_provider: OpenAICredentialProvider, pub client: Arc, pub executor: Arc, rate_limit_count_rx: watch::Receiver>, @@ -73,6 +74,7 @@ impl OpenAIEmbeddingProvider { OpenAIEmbeddingProvider { model, + credential_provider: OpenAICredentialProvider {}, client, executor, rate_limit_count_rx, @@ -138,25 +140,17 @@ impl OpenAIEmbeddingProvider { #[async_trait] impl EmbeddingProvider for OpenAIEmbeddingProvider { - fn retrieve_credentials(&self, cx: &AppContext) -> Option { - let api_key = if let Ok(api_key) = env::var("OPENAI_API_KEY") { - Some(api_key) - } else if let Some((_, api_key)) = cx - .platform() - .read_credentials(OPENAI_API_URL) - .log_err() - .flatten() - { - String::from_utf8(api_key).log_err() - } else { - None - }; - api_key - } fn base_model(&self) -> Box { let model: Box = Box::new(self.model.clone()); model } + + fn credential_provider(&self) -> Box { + let credential_provider: Box = + Box::new(self.credential_provider.clone()); + credential_provider + } + fn max_tokens_per_batch(&self) -> usize { 50000 } @@ -164,25 +158,11 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider { fn rate_limit_expiration(&self) -> Option { *self.rate_limit_count_rx.borrow() } - // fn truncate(&self, span: &str) -> (String, usize) { - // let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); - // let output = if tokens.len() > OPENAI_INPUT_LIMIT { - // tokens.truncate(OPENAI_INPUT_LIMIT); - // OPENAI_BPE_TOKENIZER - // .decode(tokens.clone()) - // .ok() - // .unwrap_or_else(|| span.to_string()) - // } else { - // span.to_string() - // }; - - // (output, tokens.len()) - // } async fn embed_batch( &self, spans: Vec, - api_key: Option, + _credential: ProviderCredential, ) -> Result> { const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; const MAX_RETRIES: usize = 4; diff --git a/crates/ai/src/providers/open_ai/mod.rs b/crates/ai/src/providers/open_ai/mod.rs index 67cb2b5315..49e29fbc8c 100644 --- a/crates/ai/src/providers/open_ai/mod.rs +++ b/crates/ai/src/providers/open_ai/mod.rs @@ -1,3 +1,4 @@ +pub mod auth; pub mod completion; pub mod embedding; pub mod model; diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs new file mode 100644 index 0000000000..d8805bad1a --- /dev/null +++ b/crates/ai/src/test.rs @@ -0,0 +1,123 @@ +use std::{ + sync::atomic::{self, AtomicUsize, Ordering}, + time::Instant, +}; + +use async_trait::async_trait; + +use crate::{ + auth::{CredentialProvider, NullCredentialProvider, ProviderCredential}, + embedding::{Embedding, EmbeddingProvider}, + models::{LanguageModel, TruncationDirection}, +}; + +#[derive(Clone)] +pub struct FakeLanguageModel { + pub capacity: usize, +} + +impl LanguageModel for FakeLanguageModel { + fn name(&self) -> String { + "dummy".to_string() + } + fn count_tokens(&self, content: &str) -> anyhow::Result { + anyhow::Ok(content.chars().collect::>().len()) + } + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result { + anyhow::Ok(match direction { + TruncationDirection::End => content.chars().collect::>()[..length] + .into_iter() + .collect::(), + TruncationDirection::Start => content.chars().collect::>()[length..] + .into_iter() + .collect::(), + }) + } + fn capacity(&self) -> anyhow::Result { + anyhow::Ok(self.capacity) + } +} + +pub struct FakeEmbeddingProvider { + pub embedding_count: AtomicUsize, + pub credential_provider: NullCredentialProvider, +} + +impl Clone for FakeEmbeddingProvider { + fn clone(&self) -> Self { + FakeEmbeddingProvider { + embedding_count: AtomicUsize::new(self.embedding_count.load(Ordering::SeqCst)), + credential_provider: self.credential_provider.clone(), + } + } +} + +impl Default for FakeEmbeddingProvider { + fn default() -> Self { + FakeEmbeddingProvider { + embedding_count: AtomicUsize::default(), + credential_provider: NullCredentialProvider {}, + } + } +} + +impl FakeEmbeddingProvider { + pub fn embedding_count(&self) -> usize { + self.embedding_count.load(atomic::Ordering::SeqCst) + } + + pub fn embed_sync(&self, span: &str) -> Embedding { + let mut result = vec![1.0; 26]; + for letter in span.chars() { + let letter = letter.to_ascii_lowercase(); + if letter as u32 >= 'a' as u32 { + let ix = (letter as u32) - ('a' as u32); + if ix < 26 { + result[ix as usize] += 1.0; + } + } + } + + let norm = result.iter().map(|x| x * x).sum::().sqrt(); + for x in &mut result { + *x /= norm; + } + + result.into() + } +} + +#[async_trait] +impl EmbeddingProvider for FakeEmbeddingProvider { + fn base_model(&self) -> Box { + Box::new(FakeLanguageModel { capacity: 1000 }) + } + fn credential_provider(&self) -> Box { + let credential_provider: Box = + Box::new(self.credential_provider.clone()); + credential_provider + } + fn max_tokens_per_batch(&self) -> usize { + 1000 + } + + fn rate_limit_expiration(&self) -> Option { + None + } + + async fn embed_batch( + &self, + spans: Vec, + _credential: ProviderCredential, + ) -> anyhow::Result> { + self.embedding_count + .fetch_add(spans.len(), atomic::Ordering::SeqCst); + + anyhow::Ok(spans.iter().map(|span| self.embed_sync(span)).collect()) + } +} diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index e535eca144..e71b1ae2cb 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -335,7 +335,6 @@ fn strip_markdown_codeblock( #[cfg(test)] mod tests { use super::*; - use ai::providers::dummy::DummyCompletionRequest; use futures::{ future::BoxFuture, stream::{self, BoxStream}, @@ -345,9 +344,21 @@ mod tests { use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point}; use parking_lot::Mutex; use rand::prelude::*; + use serde::Serialize; use settings::SettingsStore; use smol::future::FutureExt; + #[derive(Serialize)] + pub struct DummyCompletionRequest { + pub name: String, + } + + impl CompletionRequest for DummyCompletionRequest { + fn data(&self) -> serde_json::Result { + serde_json::to_string(self) + } + } + #[gpui::test(iterations = 10)] async fn test_transform_autoindent( cx: &mut TestAppContext, @@ -381,6 +392,7 @@ mod tests { cx, ) }); + let request = Box::new(DummyCompletionRequest { name: "test".to_string(), }); diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index 1febb2af78..875440ef3f 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -42,6 +42,7 @@ sha1 = "0.10.5" ndarray = { version = "0.15.0" } [dev-dependencies] +ai = { path = "../ai", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } diff --git a/crates/semantic_index/src/embedding_queue.rs b/crates/semantic_index/src/embedding_queue.rs index d57d5c7bbe..9ca6d8a0d9 100644 --- a/crates/semantic_index/src/embedding_queue.rs +++ b/crates/semantic_index/src/embedding_queue.rs @@ -1,5 +1,5 @@ use crate::{parsing::Span, JobHandle}; -use ai::embedding::EmbeddingProvider; +use ai::{auth::ProviderCredential, embedding::EmbeddingProvider}; use gpui::executor::Background; use parking_lot::Mutex; use smol::channel; @@ -41,7 +41,7 @@ pub struct EmbeddingQueue { pending_batch_token_count: usize, finished_files_tx: channel::Sender, finished_files_rx: channel::Receiver, - api_key: Option, + provider_credential: ProviderCredential, } #[derive(Clone)] @@ -54,7 +54,7 @@ impl EmbeddingQueue { pub fn new( embedding_provider: Arc, executor: Arc, - api_key: Option, + provider_credential: ProviderCredential, ) -> Self { let (finished_files_tx, finished_files_rx) = channel::unbounded(); Self { @@ -64,12 +64,12 @@ impl EmbeddingQueue { pending_batch_token_count: 0, finished_files_tx, finished_files_rx, - api_key, + provider_credential, } } - pub fn set_api_key(&mut self, api_key: Option) { - self.api_key = api_key + pub fn set_credential(&mut self, credential: ProviderCredential) { + self.provider_credential = credential } pub fn push(&mut self, file: FileToEmbed) { @@ -118,7 +118,7 @@ impl EmbeddingQueue { let finished_files_tx = self.finished_files_tx.clone(); let embedding_provider = self.embedding_provider.clone(); - let api_key = self.api_key.clone(); + let credential = self.provider_credential.clone(); self.executor .spawn(async move { @@ -143,7 +143,7 @@ impl EmbeddingQueue { return; }; - match embedding_provider.embed_batch(spans, api_key).await { + match embedding_provider.embed_batch(spans, credential).await { Ok(embeddings) => { let mut embeddings = embeddings.into_iter(); for fragment in batch { diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 6863918d5d..5be3d6ccf5 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -7,6 +7,7 @@ pub mod semantic_index_settings; mod semantic_index_tests; use crate::semantic_index_settings::SemanticIndexSettings; +use ai::auth::ProviderCredential; use ai::embedding::{Embedding, EmbeddingProvider}; use ai::providers::open_ai::OpenAIEmbeddingProvider; use anyhow::{anyhow, Result}; @@ -124,7 +125,7 @@ pub struct SemanticIndex { _embedding_task: Task<()>, _parsing_files_tasks: Vec>, projects: HashMap, ProjectState>, - api_key: Option, + provider_credential: ProviderCredential, embedding_queue: Arc>, } @@ -279,18 +280,27 @@ impl SemanticIndex { } } - pub fn authenticate(&mut self, cx: &AppContext) { - if self.api_key.is_none() { - self.api_key = self.embedding_provider.retrieve_credentials(cx); - - self.embedding_queue - .lock() - .set_api_key(self.api_key.clone()); + pub fn authenticate(&mut self, cx: &AppContext) -> bool { + let credential = self.provider_credential.clone(); + match credential { + ProviderCredential::NoCredentials => { + let credential = self.embedding_provider.retrieve_credentials(cx); + self.provider_credential = credential; + } + _ => {} } + + self.embedding_queue.lock().set_credential(credential); + + self.is_authenticated() } pub fn is_authenticated(&self) -> bool { - self.api_key.is_some() + let credential = &self.provider_credential; + match credential { + &ProviderCredential::Credentials { .. } => true, + _ => false, + } } pub fn enabled(cx: &AppContext) -> bool { @@ -340,7 +350,7 @@ impl SemanticIndex { Ok(cx.add_model(|cx| { let t0 = Instant::now(); let embedding_queue = - EmbeddingQueue::new(embedding_provider.clone(), cx.background().clone(), None); + EmbeddingQueue::new(embedding_provider.clone(), cx.background().clone(), ProviderCredential::NoCredentials); let _embedding_task = cx.background().spawn({ let embedded_files = embedding_queue.finished_files(); let db = db.clone(); @@ -405,7 +415,7 @@ impl SemanticIndex { _embedding_task, _parsing_files_tasks, projects: Default::default(), - api_key: None, + provider_credential: ProviderCredential::NoCredentials, embedding_queue } })) @@ -721,13 +731,14 @@ impl SemanticIndex { let index = self.index_project(project.clone(), cx); let embedding_provider = self.embedding_provider.clone(); - let api_key = self.api_key.clone(); + let credential = self.provider_credential.clone(); cx.spawn(|this, mut cx| async move { index.await?; let t0 = Instant::now(); + let query = embedding_provider - .embed_batch(vec![query], api_key) + .embed_batch(vec![query], credential) .await? .pop() .ok_or_else(|| anyhow!("could not embed query"))?; @@ -945,7 +956,7 @@ impl SemanticIndex { let fs = self.fs.clone(); let db_path = self.db.path().clone(); let background = cx.background().clone(); - let api_key = self.api_key.clone(); + let credential = self.provider_credential.clone(); cx.background().spawn(async move { let db = VectorDatabase::new(fs, db_path.clone(), background).await?; let mut results = Vec::::new(); @@ -964,7 +975,7 @@ impl SemanticIndex { &mut spans, embedding_provider.as_ref(), &db, - api_key.clone(), + credential.clone(), ) .await .log_err() @@ -1008,9 +1019,8 @@ impl SemanticIndex { project: ModelHandle, cx: &mut ModelContext, ) -> Task> { - if self.api_key.is_none() { - self.authenticate(cx); - if self.api_key.is_none() { + if !self.is_authenticated() { + if !self.authenticate(cx) { return Task::ready(Err(anyhow!("user is not authenticated"))); } } @@ -1193,7 +1203,7 @@ impl SemanticIndex { spans: &mut [Span], embedding_provider: &dyn EmbeddingProvider, db: &VectorDatabase, - api_key: Option, + credential: ProviderCredential, ) -> Result<()> { let mut batch = Vec::new(); let mut batch_tokens = 0; @@ -1216,7 +1226,7 @@ impl SemanticIndex { if batch_tokens + span.token_count > embedding_provider.max_tokens_per_batch() { let batch_embeddings = embedding_provider - .embed_batch(mem::take(&mut batch), api_key.clone()) + .embed_batch(mem::take(&mut batch), credential.clone()) .await?; embeddings.extend(batch_embeddings); batch_tokens = 0; @@ -1228,7 +1238,7 @@ impl SemanticIndex { if !batch.is_empty() { let batch_embeddings = embedding_provider - .embed_batch(mem::take(&mut batch), api_key) + .embed_batch(mem::take(&mut batch), credential) .await?; embeddings.extend(batch_embeddings); diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 1c117c9ea2..7d5a4e22e8 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -4,14 +4,9 @@ use crate::{ semantic_index_settings::SemanticIndexSettings, FileToEmbed, JobHandle, SearchResult, SemanticIndex, EMBEDDING_QUEUE_FLUSH_TIMEOUT, }; -use ai::providers::dummy::{DummyEmbeddingProvider, DummyLanguageModel}; -use ai::{ - embedding::{Embedding, EmbeddingProvider}, - models::LanguageModel, -}; -use anyhow::Result; -use async_trait::async_trait; -use gpui::{executor::Deterministic, AppContext, Task, TestAppContext}; +use ai::test::FakeEmbeddingProvider; + +use gpui::{executor::Deterministic, Task, TestAppContext}; use language::{Language, LanguageConfig, LanguageRegistry, ToOffset}; use parking_lot::Mutex; use pretty_assertions::assert_eq; @@ -19,14 +14,7 @@ use project::{project_settings::ProjectSettings, search::PathMatcher, FakeFs, Fs use rand::{rngs::StdRng, Rng}; use serde_json::json; use settings::SettingsStore; -use std::{ - path::Path, - sync::{ - atomic::{self, AtomicUsize}, - Arc, - }, - time::{Instant, SystemTime}, -}; +use std::{path::Path, sync::Arc, time::SystemTime}; use unindent::Unindent; use util::RandomCharIter; @@ -232,7 +220,11 @@ async fn test_embedding_batching(cx: &mut TestAppContext, mut rng: StdRng) { let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); - let mut queue = EmbeddingQueue::new(embedding_provider.clone(), cx.background(), None); + let mut queue = EmbeddingQueue::new( + embedding_provider.clone(), + cx.background(), + ai::auth::ProviderCredential::NoCredentials, + ); for file in &files { queue.push(file.clone()); } @@ -284,7 +276,7 @@ fn assert_search_results( #[gpui::test] async fn test_code_context_retrieval_rust() { let language = rust_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = " @@ -386,7 +378,7 @@ async fn test_code_context_retrieval_rust() { #[gpui::test] async fn test_code_context_retrieval_json() { let language = json_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -470,7 +462,7 @@ fn assert_documents_eq( #[gpui::test] async fn test_code_context_retrieval_javascript() { let language = js_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = " @@ -569,7 +561,7 @@ async fn test_code_context_retrieval_javascript() { #[gpui::test] async fn test_code_context_retrieval_lua() { let language = lua_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -643,7 +635,7 @@ async fn test_code_context_retrieval_lua() { #[gpui::test] async fn test_code_context_retrieval_elixir() { let language = elixir_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -760,7 +752,7 @@ async fn test_code_context_retrieval_elixir() { #[gpui::test] async fn test_code_context_retrieval_cpp() { let language = cpp_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = " @@ -913,7 +905,7 @@ async fn test_code_context_retrieval_cpp() { #[gpui::test] async fn test_code_context_retrieval_ruby() { let language = ruby_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -1104,7 +1096,7 @@ async fn test_code_context_retrieval_ruby() { #[gpui::test] async fn test_code_context_retrieval_php() { let language = php_lang(); - let embedding_provider = Arc::new(DummyEmbeddingProvider {}); + let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); let mut retriever = CodeContextRetriever::new(embedding_provider); let text = r#" @@ -1252,65 +1244,6 @@ async fn test_code_context_retrieval_php() { ); } -#[derive(Default)] -struct FakeEmbeddingProvider { - embedding_count: AtomicUsize, -} - -impl FakeEmbeddingProvider { - fn embedding_count(&self) -> usize { - self.embedding_count.load(atomic::Ordering::SeqCst) - } - - fn embed_sync(&self, span: &str) -> Embedding { - let mut result = vec![1.0; 26]; - for letter in span.chars() { - let letter = letter.to_ascii_lowercase(); - if letter as u32 >= 'a' as u32 { - let ix = (letter as u32) - ('a' as u32); - if ix < 26 { - result[ix as usize] += 1.0; - } - } - } - - let norm = result.iter().map(|x| x * x).sum::().sqrt(); - for x in &mut result { - *x /= norm; - } - - result.into() - } -} - -#[async_trait] -impl EmbeddingProvider for FakeEmbeddingProvider { - fn base_model(&self) -> Box { - Box::new(DummyLanguageModel {}) - } - fn retrieve_credentials(&self, _cx: &AppContext) -> Option { - Some("Fake Credentials".to_string()) - } - fn max_tokens_per_batch(&self) -> usize { - 1000 - } - - fn rate_limit_expiration(&self) -> Option { - None - } - - async fn embed_batch( - &self, - spans: Vec, - _api_key: Option, - ) -> Result> { - self.embedding_count - .fetch_add(spans.len(), atomic::Ordering::SeqCst); - - anyhow::Ok(spans.iter().map(|span| self.embed_sync(span)).collect()) - } -} - fn js_lang() -> Arc { Arc::new( Language::new( From ca82ec8e8e2484099de3020233804671387ba9de Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 26 Oct 2023 14:05:55 +0200 Subject: [PATCH 10/54] fixed truncation error in fake language model --- crates/ai/src/auth.rs | 2 +- crates/ai/src/test.rs | 4 ++++ crates/semantic_index/src/embedding_queue.rs | 2 +- crates/semantic_index/src/semantic_index.rs | 5 ++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/ai/src/auth.rs b/crates/ai/src/auth.rs index a3ce8aece1..c188c30797 100644 --- a/crates/ai/src/auth.rs +++ b/crates/ai/src/auth.rs @@ -1,6 +1,6 @@ use gpui::AppContext; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ProviderCredential { Credentials { api_key: String }, NoCredentials, diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index d8805bad1a..bc143e3c21 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -29,6 +29,10 @@ impl LanguageModel for FakeLanguageModel { length: usize, direction: TruncationDirection, ) -> anyhow::Result { + if length > self.count_tokens(content)? { + return anyhow::Ok(content.to_string()); + } + anyhow::Ok(match direction { TruncationDirection::End => content.chars().collect::>()[..length] .into_iter() diff --git a/crates/semantic_index/src/embedding_queue.rs b/crates/semantic_index/src/embedding_queue.rs index 9ca6d8a0d9..299aa328b5 100644 --- a/crates/semantic_index/src/embedding_queue.rs +++ b/crates/semantic_index/src/embedding_queue.rs @@ -69,7 +69,7 @@ impl EmbeddingQueue { } pub fn set_credential(&mut self, credential: ProviderCredential) { - self.provider_credential = credential + self.provider_credential = credential; } pub fn push(&mut self, file: FileToEmbed) { diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 5be3d6ccf5..f420e0503b 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -291,7 +291,6 @@ impl SemanticIndex { } self.embedding_queue.lock().set_credential(credential); - self.is_authenticated() } @@ -299,6 +298,7 @@ impl SemanticIndex { let credential = &self.provider_credential; match credential { &ProviderCredential::Credentials { .. } => true, + &ProviderCredential::NotNeeded => true, _ => false, } } @@ -1020,11 +1020,14 @@ impl SemanticIndex { cx: &mut ModelContext, ) -> Task> { if !self.is_authenticated() { + println!("Authenticating"); if !self.authenticate(cx) { return Task::ready(Err(anyhow!("user is not authenticated"))); } } + println!("SHOULD NOW BE AUTHENTICATED"); + if !self.projects.contains_key(&project.downgrade()) { let subscription = cx.subscribe(&project, |this, project, event, cx| match event { project::Event::WorktreeAdded | project::Event::WorktreeRemoved(_) => { From 6c8bb4b05e62aab88d5487cecb215c2fe8863a49 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Fri, 27 Oct 2023 08:33:35 +0200 Subject: [PATCH 11/54] ensure OpenAIEmbeddingProvider is using the provider credentials --- crates/ai/src/providers/open_ai/embedding.rs | 11 ++++++----- crates/semantic_index/src/embedding_queue.rs | 2 +- crates/semantic_index/src/semantic_index.rs | 17 ++++++----------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index 1385b32b4d..9806877660 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -162,14 +162,15 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider { async fn embed_batch( &self, spans: Vec, - _credential: ProviderCredential, + credential: ProviderCredential, ) -> Result> { const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; const MAX_RETRIES: usize = 4; - let api_key = OPENAI_API_KEY - .as_ref() - .ok_or_else(|| anyhow!("no api key"))?; + let api_key = match credential { + ProviderCredential::Credentials { api_key } => anyhow::Ok(api_key), + _ => Err(anyhow!("no api key provided")), + }?; let mut request_number = 0; let mut rate_limiting = false; @@ -178,7 +179,7 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider { while request_number < MAX_RETRIES { response = self .send_request( - api_key, + &api_key, spans.iter().map(|x| &**x).collect(), request_timeout, ) diff --git a/crates/semantic_index/src/embedding_queue.rs b/crates/semantic_index/src/embedding_queue.rs index 299aa328b5..6f792c78e2 100644 --- a/crates/semantic_index/src/embedding_queue.rs +++ b/crates/semantic_index/src/embedding_queue.rs @@ -41,7 +41,7 @@ pub struct EmbeddingQueue { pending_batch_token_count: usize, finished_files_tx: channel::Sender, finished_files_rx: channel::Receiver, - provider_credential: ProviderCredential, + pub provider_credential: ProviderCredential, } #[derive(Clone)] diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index f420e0503b..7fb5f749b4 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -281,15 +281,13 @@ impl SemanticIndex { } pub fn authenticate(&mut self, cx: &AppContext) -> bool { - let credential = self.provider_credential.clone(); - match credential { - ProviderCredential::NoCredentials => { - let credential = self.embedding_provider.retrieve_credentials(cx); - self.provider_credential = credential; - } - _ => {} - } + let existing_credential = self.provider_credential.clone(); + let credential = match existing_credential { + ProviderCredential::NoCredentials => self.embedding_provider.retrieve_credentials(cx), + _ => existing_credential, + }; + self.provider_credential = credential.clone(); self.embedding_queue.lock().set_credential(credential); self.is_authenticated() } @@ -1020,14 +1018,11 @@ impl SemanticIndex { cx: &mut ModelContext, ) -> Task> { if !self.is_authenticated() { - println!("Authenticating"); if !self.authenticate(cx) { return Task::ready(Err(anyhow!("user is not authenticated"))); } } - println!("SHOULD NOW BE AUTHENTICATED"); - if !self.projects.contains_key(&project.downgrade()) { let subscription = cx.subscribe(&project, |this, project, event, cx| match event { project::Event::WorktreeAdded | project::Event::WorktreeRemoved(_) => { From ec9d79b6fec4e90f34367bd3a855ef11c58f75fd Mon Sep 17 00:00:00 2001 From: KCaverly Date: Fri, 27 Oct 2023 08:51:30 +0200 Subject: [PATCH 12/54] add concept of LanguageModel to CompletionProvider --- crates/ai/src/completion.rs | 3 +++ crates/ai/src/providers/open_ai/completion.rs | 21 ++++++++++++++++--- crates/ai/src/providers/open_ai/embedding.rs | 1 - crates/assistant/src/assistant_panel.rs | 1 + crates/assistant/src/codegen.rs | 5 +++++ 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index ba89c869d2..da9ebd5a1d 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -1,11 +1,14 @@ use anyhow::Result; use futures::{future::BoxFuture, stream::BoxStream}; +use crate::models::LanguageModel; + pub trait CompletionRequest: Send + Sync { fn data(&self) -> serde_json::Result; } pub trait CompletionProvider { + fn base_model(&self) -> Box; fn complete( &self, prompt: Box, diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs index 95ed13c0dd..20f72c0ff7 100644 --- a/crates/ai/src/providers/open_ai/completion.rs +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -12,7 +12,12 @@ use std::{ sync::Arc, }; -use crate::completion::{CompletionProvider, CompletionRequest}; +use crate::{ + completion::{CompletionProvider, CompletionRequest}, + models::LanguageModel, +}; + +use super::OpenAILanguageModel; pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; @@ -180,17 +185,27 @@ pub async fn stream_completion( } pub struct OpenAICompletionProvider { + model: OpenAILanguageModel, api_key: String, executor: Arc, } impl OpenAICompletionProvider { - pub fn new(api_key: String, executor: Arc) -> Self { - Self { api_key, executor } + pub fn new(model_name: &str, api_key: String, executor: Arc) -> Self { + let model = OpenAILanguageModel::load(model_name); + Self { + model, + api_key, + executor, + } } } impl CompletionProvider for OpenAICompletionProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(self.model.clone()); + model + } fn complete( &self, prompt: Box, diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index 9806877660..64f568da1a 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -26,7 +26,6 @@ use crate::providers::open_ai::OpenAILanguageModel; use crate::providers::open_ai::auth::OpenAICredentialProvider; lazy_static! { - static ref OPENAI_API_KEY: Option = env::var("OPENAI_API_KEY").ok(); static ref OPENAI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap(); } diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index ec16c8fd04..c899465ed2 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -328,6 +328,7 @@ impl AssistantPanel { let inline_assist_id = post_inc(&mut self.next_inline_assist_id); let provider = Arc::new(OpenAICompletionProvider::new( + "gpt-4", api_key, cx.background().clone(), )); diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index e71b1ae2cb..33adb2e570 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -335,6 +335,7 @@ fn strip_markdown_codeblock( #[cfg(test)] mod tests { use super::*; + use ai::{models::LanguageModel, test::FakeLanguageModel}; use futures::{ future::BoxFuture, stream::{self, BoxStream}, @@ -638,6 +639,10 @@ mod tests { } impl CompletionProvider for TestCompletionProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(FakeLanguageModel { capacity: 8190 }); + model + } fn complete( &self, _prompt: Box, From 7af77b1cf95da45314092aa35f7bcc04fa4fd3bc Mon Sep 17 00:00:00 2001 From: KCaverly Date: Fri, 27 Oct 2023 12:26:01 +0200 Subject: [PATCH 13/54] moved TestCompletionProvider into ai --- crates/ai/src/test.rs | 39 +++++++++++++++++++++++++++++++++ crates/assistant/Cargo.toml | 1 + crates/assistant/src/codegen.rs | 38 +------------------------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index bc143e3c21..2c78027b62 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -4,9 +4,12 @@ use std::{ }; use async_trait::async_trait; +use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt}; +use parking_lot::Mutex; use crate::{ auth::{CredentialProvider, NullCredentialProvider, ProviderCredential}, + completion::{CompletionProvider, CompletionRequest}, embedding::{Embedding, EmbeddingProvider}, models::{LanguageModel, TruncationDirection}, }; @@ -125,3 +128,39 @@ impl EmbeddingProvider for FakeEmbeddingProvider { anyhow::Ok(spans.iter().map(|span| self.embed_sync(span)).collect()) } } + +pub struct TestCompletionProvider { + last_completion_tx: Mutex>>, +} + +impl TestCompletionProvider { + pub fn new() -> Self { + Self { + last_completion_tx: Mutex::new(None), + } + } + + pub fn send_completion(&self, completion: impl Into) { + let mut tx = self.last_completion_tx.lock(); + tx.as_mut().unwrap().try_send(completion.into()).unwrap(); + } + + pub fn finish_completion(&self) { + self.last_completion_tx.lock().take().unwrap(); + } +} + +impl CompletionProvider for TestCompletionProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(FakeLanguageModel { capacity: 8190 }); + model + } + fn complete( + &self, + _prompt: Box, + ) -> BoxFuture<'static, anyhow::Result>>> { + let (tx, rx) = mpsc::channel(1); + *self.last_completion_tx.lock() = Some(tx); + async move { Ok(rx.map(|rx| Ok(rx)).boxed()) }.boxed() + } +} diff --git a/crates/assistant/Cargo.toml b/crates/assistant/Cargo.toml index 9cfdd3301a..6b0ce659e3 100644 --- a/crates/assistant/Cargo.toml +++ b/crates/assistant/Cargo.toml @@ -44,6 +44,7 @@ tiktoken-rs = "0.5" [dev-dependencies] editor = { path = "../editor", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +ai = { path = "../ai", features = ["test-support"]} ctor.workspace = true env_logger.workspace = true diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index 33adb2e570..3516fc3708 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -335,7 +335,7 @@ fn strip_markdown_codeblock( #[cfg(test)] mod tests { use super::*; - use ai::{models::LanguageModel, test::FakeLanguageModel}; + use ai::test::TestCompletionProvider; use futures::{ future::BoxFuture, stream::{self, BoxStream}, @@ -617,42 +617,6 @@ mod tests { } } - struct TestCompletionProvider { - last_completion_tx: Mutex>>, - } - - impl TestCompletionProvider { - fn new() -> Self { - Self { - last_completion_tx: Mutex::new(None), - } - } - - fn send_completion(&self, completion: impl Into) { - let mut tx = self.last_completion_tx.lock(); - tx.as_mut().unwrap().try_send(completion.into()).unwrap(); - } - - fn finish_completion(&self) { - self.last_completion_tx.lock().take().unwrap(); - } - } - - impl CompletionProvider for TestCompletionProvider { - fn base_model(&self) -> Box { - let model: Box = Box::new(FakeLanguageModel { capacity: 8190 }); - model - } - fn complete( - &self, - _prompt: Box, - ) -> BoxFuture<'static, Result>>> { - let (tx, rx) = mpsc::channel(1); - *self.last_completion_tx.lock() = Some(tx); - async move { Ok(rx.map(|rx| Ok(rx)).boxed()) }.boxed() - } - } - fn rust_lang() -> Language { Language::new( LanguageConfig { From 558f54c424a1b0f7ccaa317af62b50fd5c467fc0 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sat, 28 Oct 2023 16:35:43 -0400 Subject: [PATCH 14/54] added credential provider to completion provider --- crates/ai/src/completion.rs | 10 +++++++++- crates/ai/src/providers/open_ai/completion.rs | 10 +++++++++- crates/ai/src/providers/open_ai/embedding.rs | 1 - crates/ai/src/test.rs | 3 +++ crates/assistant/src/codegen.rs | 7 +------ 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index da9ebd5a1d..5b9bad4870 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -1,7 +1,11 @@ use anyhow::Result; use futures::{future::BoxFuture, stream::BoxStream}; +use gpui::AppContext; -use crate::models::LanguageModel; +use crate::{ + auth::{CredentialProvider, ProviderCredential}, + models::LanguageModel, +}; pub trait CompletionRequest: Send + Sync { fn data(&self) -> serde_json::Result; @@ -9,6 +13,10 @@ pub trait CompletionRequest: Send + Sync { pub trait CompletionProvider { fn base_model(&self) -> Box; + fn credential_provider(&self) -> Box; + fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { + self.credential_provider().retrieve_credentials(cx) + } fn complete( &self, prompt: Box, diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs index 20f72c0ff7..9c9d205ff7 100644 --- a/crates/ai/src/providers/open_ai/completion.rs +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -13,11 +13,12 @@ use std::{ }; use crate::{ + auth::CredentialProvider, completion::{CompletionProvider, CompletionRequest}, models::LanguageModel, }; -use super::OpenAILanguageModel; +use super::{auth::OpenAICredentialProvider, OpenAILanguageModel}; pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; @@ -186,6 +187,7 @@ pub async fn stream_completion( pub struct OpenAICompletionProvider { model: OpenAILanguageModel, + credential_provider: OpenAICredentialProvider, api_key: String, executor: Arc, } @@ -193,8 +195,10 @@ pub struct OpenAICompletionProvider { impl OpenAICompletionProvider { pub fn new(model_name: &str, api_key: String, executor: Arc) -> Self { let model = OpenAILanguageModel::load(model_name); + let credential_provider = OpenAICredentialProvider {}; Self { model, + credential_provider, api_key, executor, } @@ -206,6 +210,10 @@ impl CompletionProvider for OpenAICompletionProvider { let model: Box = Box::new(self.model.clone()); model } + fn credential_provider(&self) -> Box { + let provider: Box = Box::new(self.credential_provider.clone()); + provider + } fn complete( &self, prompt: Box, diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index 64f568da1a..dafc94580d 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -11,7 +11,6 @@ use parking_lot::Mutex; use parse_duration::parse; use postage::watch; use serde::{Deserialize, Serialize}; -use std::env; use std::ops::Add; use std::sync::Arc; use std::time::{Duration, Instant}; diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index 2c78027b62..b8f99af400 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -155,6 +155,9 @@ impl CompletionProvider for TestCompletionProvider { let model: Box = Box::new(FakeLanguageModel { capacity: 8190 }); model } + fn credential_provider(&self) -> Box { + Box::new(NullCredentialProvider {}) + } fn complete( &self, _prompt: Box, diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index 3516fc3708..7f4c95f655 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -336,18 +336,13 @@ fn strip_markdown_codeblock( mod tests { use super::*; use ai::test::TestCompletionProvider; - use futures::{ - future::BoxFuture, - stream::{self, BoxStream}, - }; + use futures::stream::{self}; use gpui::{executor::Deterministic, TestAppContext}; use indoc::indoc; use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point}; - use parking_lot::Mutex; use rand::prelude::*; use serde::Serialize; use settings::SettingsStore; - use smol::future::FutureExt; #[derive(Serialize)] pub struct DummyCompletionRequest { From 1e8b23d8fb9cf231de56ed25ebd56ea04190fc55 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Sat, 28 Oct 2023 18:16:45 -0400 Subject: [PATCH 15/54] replace api_key with ProviderCredential throughout the AssistantPanel --- crates/ai/src/auth.rs | 4 + crates/ai/src/completion.rs | 6 + crates/ai/src/providers/open_ai/auth.rs | 13 + crates/ai/src/providers/open_ai/completion.rs | 24 +- crates/assistant/src/assistant_panel.rs | 282 +++++++++++------- 5 files changed, 208 insertions(+), 121 deletions(-) diff --git a/crates/ai/src/auth.rs b/crates/ai/src/auth.rs index c188c30797..cb3f2beabb 100644 --- a/crates/ai/src/auth.rs +++ b/crates/ai/src/auth.rs @@ -9,6 +9,8 @@ pub enum ProviderCredential { pub trait CredentialProvider: Send + Sync { fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential; + fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential); + fn delete_credentials(&self, cx: &AppContext); } #[derive(Clone)] @@ -17,4 +19,6 @@ impl CredentialProvider for NullCredentialProvider { fn retrieve_credentials(&self, _cx: &AppContext) -> ProviderCredential { ProviderCredential::NotNeeded } + fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) {} + fn delete_credentials(&self, cx: &AppContext) {} } diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index 5b9bad4870..6a2806a5cb 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -17,6 +17,12 @@ pub trait CompletionProvider { fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { self.credential_provider().retrieve_credentials(cx) } + fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) { + self.credential_provider().save_credentials(cx, credential); + } + fn delete_credentials(&self, cx: &AppContext) { + self.credential_provider().delete_credentials(cx); + } fn complete( &self, prompt: Box, diff --git a/crates/ai/src/providers/open_ai/auth.rs b/crates/ai/src/providers/open_ai/auth.rs index c817ffea00..7cb51ab449 100644 --- a/crates/ai/src/providers/open_ai/auth.rs +++ b/crates/ai/src/providers/open_ai/auth.rs @@ -30,4 +30,17 @@ impl CredentialProvider for OpenAICredentialProvider { ProviderCredential::NoCredentials } } + fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) { + match credential { + ProviderCredential::Credentials { api_key } => { + cx.platform() + .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) + .log_err(); + } + _ => {} + } + } + fn delete_credentials(&self, cx: &AppContext) { + cx.platform().delete_credentials(OPENAI_API_URL).log_err(); + } } diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs index 9c9d205ff7..febe491123 100644 --- a/crates/ai/src/providers/open_ai/completion.rs +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -13,7 +13,7 @@ use std::{ }; use crate::{ - auth::CredentialProvider, + auth::{CredentialProvider, ProviderCredential}, completion::{CompletionProvider, CompletionRequest}, models::LanguageModel, }; @@ -102,10 +102,17 @@ pub struct OpenAIResponseStreamEvent { } pub async fn stream_completion( - api_key: String, + credential: ProviderCredential, executor: Arc, request: Box, ) -> Result>> { + let api_key = match credential { + ProviderCredential::Credentials { api_key } => api_key, + _ => { + return Err(anyhow!("no credentials provider for completion")); + } + }; + let (tx, rx) = futures::channel::mpsc::unbounded::>(); let json_data = request.data()?; @@ -188,18 +195,22 @@ pub async fn stream_completion( pub struct OpenAICompletionProvider { model: OpenAILanguageModel, credential_provider: OpenAICredentialProvider, - api_key: String, + credential: ProviderCredential, executor: Arc, } impl OpenAICompletionProvider { - pub fn new(model_name: &str, api_key: String, executor: Arc) -> Self { + pub fn new( + model_name: &str, + credential: ProviderCredential, + executor: Arc, + ) -> Self { let model = OpenAILanguageModel::load(model_name); let credential_provider = OpenAICredentialProvider {}; Self { model, credential_provider, - api_key, + credential, executor, } } @@ -218,7 +229,8 @@ impl CompletionProvider for OpenAICompletionProvider { &self, prompt: Box, ) -> BoxFuture<'static, Result>>> { - let request = stream_completion(self.api_key.clone(), self.executor.clone(), prompt); + let credential = self.credential.clone(); + let request = stream_completion(credential, self.executor.clone(), prompt); async move { let response = request.await?; let stream = response diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index c899465ed2..f9187b8785 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -7,7 +7,8 @@ use crate::{ }; use ai::{ - completion::CompletionRequest, + auth::ProviderCredential, + completion::{CompletionProvider, CompletionRequest}, providers::open_ai::{ stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, }, @@ -100,8 +101,8 @@ pub fn init(cx: &mut AppContext) { cx.capture_action(ConversationEditor::copy); cx.add_action(ConversationEditor::split); cx.capture_action(ConversationEditor::cycle_message_role); - cx.add_action(AssistantPanel::save_api_key); - cx.add_action(AssistantPanel::reset_api_key); + cx.add_action(AssistantPanel::save_credentials); + cx.add_action(AssistantPanel::reset_credentials); cx.add_action(AssistantPanel::toggle_zoom); cx.add_action(AssistantPanel::deploy); cx.add_action(AssistantPanel::select_next_match); @@ -143,7 +144,8 @@ pub struct AssistantPanel { zoomed: bool, has_focus: bool, toolbar: ViewHandle, - api_key: Rc>>, + credential: Rc>, + completion_provider: Box, api_key_editor: Option>, has_read_credentials: bool, languages: Arc, @@ -205,6 +207,12 @@ impl AssistantPanel { }); let semantic_index = SemanticIndex::global(cx); + // Defaulting currently to GPT4, allow for this to be set via config. + let completion_provider = Box::new(OpenAICompletionProvider::new( + "gpt-4", + ProviderCredential::NoCredentials, + cx.background().clone(), + )); let mut this = Self { workspace: workspace_handle, @@ -216,7 +224,8 @@ impl AssistantPanel { zoomed: false, has_focus: false, toolbar, - api_key: Rc::new(RefCell::new(None)), + credential: Rc::new(RefCell::new(ProviderCredential::NoCredentials)), + completion_provider, api_key_editor: None, has_read_credentials: false, languages: workspace.app_state().languages.clone(), @@ -257,10 +266,7 @@ impl AssistantPanel { cx: &mut ViewContext, ) { let this = if let Some(this) = workspace.panel::(cx) { - if this - .update(cx, |assistant, cx| assistant.load_api_key(cx)) - .is_some() - { + if this.update(cx, |assistant, cx| assistant.has_credentials(cx)) { this } else { workspace.focus_panel::(cx); @@ -292,12 +298,7 @@ impl AssistantPanel { cx: &mut ViewContext, project: &ModelHandle, ) { - let api_key = if let Some(api_key) = self.api_key.borrow().clone() { - api_key - } else { - return; - }; - + let credential = self.credential.borrow().clone(); let selection = editor.read(cx).selections.newest_anchor().clone(); if selection.start.excerpt_id() != selection.end.excerpt_id() { return; @@ -329,7 +330,7 @@ impl AssistantPanel { let inline_assist_id = post_inc(&mut self.next_inline_assist_id); let provider = Arc::new(OpenAICompletionProvider::new( "gpt-4", - api_key, + credential, cx.background().clone(), )); @@ -816,7 +817,7 @@ impl AssistantPanel { fn new_conversation(&mut self, cx: &mut ViewContext) -> ViewHandle { let editor = cx.add_view(|cx| { ConversationEditor::new( - self.api_key.clone(), + self.credential.clone(), self.languages.clone(), self.fs.clone(), self.workspace.clone(), @@ -875,17 +876,20 @@ impl AssistantPanel { } } - fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { + fn save_credentials(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { if let Some(api_key) = self .api_key_editor .as_ref() .map(|editor| editor.read(cx).text(cx)) { if !api_key.is_empty() { - cx.platform() - .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) - .log_err(); - *self.api_key.borrow_mut() = Some(api_key); + let credential = ProviderCredential::Credentials { + api_key: api_key.clone(), + }; + self.completion_provider + .save_credentials(cx, credential.clone()); + *self.credential.borrow_mut() = credential; + self.api_key_editor.take(); cx.focus_self(); cx.notify(); @@ -895,9 +899,9 @@ impl AssistantPanel { } } - fn reset_api_key(&mut self, _: &ResetKey, cx: &mut ViewContext) { - cx.platform().delete_credentials(OPENAI_API_URL).log_err(); - self.api_key.take(); + fn reset_credentials(&mut self, _: &ResetKey, cx: &mut ViewContext) { + self.completion_provider.delete_credentials(cx); + *self.credential.borrow_mut() = ProviderCredential::NoCredentials; self.api_key_editor = Some(build_api_key_editor(cx)); cx.focus_self(); cx.notify(); @@ -1156,13 +1160,19 @@ impl AssistantPanel { let fs = self.fs.clone(); let workspace = self.workspace.clone(); - let api_key = self.api_key.clone(); + let credential = self.credential.clone(); let languages = self.languages.clone(); cx.spawn(|this, mut cx| async move { let saved_conversation = fs.load(&path).await?; let saved_conversation = serde_json::from_str(&saved_conversation)?; let conversation = cx.add_model(|cx| { - Conversation::deserialize(saved_conversation, path.clone(), api_key, languages, cx) + Conversation::deserialize( + saved_conversation, + path.clone(), + credential, + languages, + cx, + ) }); this.update(&mut cx, |this, cx| { // If, by the time we've loaded the conversation, the user has already opened @@ -1186,30 +1196,39 @@ impl AssistantPanel { .position(|editor| editor.read(cx).conversation.read(cx).path.as_deref() == Some(path)) } - fn load_api_key(&mut self, cx: &mut ViewContext) -> Option { - if self.api_key.borrow().is_none() && !self.has_read_credentials { - self.has_read_credentials = true; - let api_key = if let Ok(api_key) = env::var("OPENAI_API_KEY") { - Some(api_key) - } else if let Some((_, api_key)) = cx - .platform() - .read_credentials(OPENAI_API_URL) - .log_err() - .flatten() - { - String::from_utf8(api_key).log_err() - } else { - None - }; - if let Some(api_key) = api_key { - *self.api_key.borrow_mut() = Some(api_key); - } else if self.api_key_editor.is_none() { - self.api_key_editor = Some(build_api_key_editor(cx)); - cx.notify(); + fn has_credentials(&mut self, cx: &mut ViewContext) -> bool { + let credential = self.load_credentials(cx); + match credential { + ProviderCredential::Credentials { .. } => true, + ProviderCredential::NotNeeded => true, + ProviderCredential::NoCredentials => false, + } + } + + fn load_credentials(&mut self, cx: &mut ViewContext) -> ProviderCredential { + let existing_credential = self.credential.clone(); + let existing_credential = existing_credential.borrow().clone(); + match existing_credential { + ProviderCredential::NoCredentials => { + if !self.has_read_credentials { + self.has_read_credentials = true; + let retrieved_credentials = self.completion_provider.retrieve_credentials(cx); + + match retrieved_credentials { + ProviderCredential::NoCredentials {} => { + self.api_key_editor = Some(build_api_key_editor(cx)); + cx.notify(); + } + _ => { + *self.credential.borrow_mut() = retrieved_credentials; + } + } + } } + _ => {} } - self.api_key.borrow().clone() + self.credential.borrow().clone() } } @@ -1394,7 +1413,7 @@ impl Panel for AssistantPanel { fn set_active(&mut self, active: bool, cx: &mut ViewContext) { if active { - self.load_api_key(cx); + self.load_credentials(cx); if self.editors.is_empty() { self.new_conversation(cx); @@ -1459,7 +1478,7 @@ struct Conversation { token_count: Option, max_token_count: usize, pending_token_count: Task>, - api_key: Rc>>, + credential: Rc>, pending_save: Task>, path: Option, _subscriptions: Vec, @@ -1471,7 +1490,8 @@ impl Entity for Conversation { impl Conversation { fn new( - api_key: Rc>>, + credential: Rc>, + language_registry: Arc, cx: &mut ModelContext, ) -> Self { @@ -1512,7 +1532,7 @@ impl Conversation { _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], pending_save: Task::ready(Ok(())), path: None, - api_key, + credential, buffer, }; let message = MessageAnchor { @@ -1559,7 +1579,7 @@ impl Conversation { fn deserialize( saved_conversation: SavedConversation, path: PathBuf, - api_key: Rc>>, + credential: Rc>, language_registry: Arc, cx: &mut ModelContext, ) -> Self { @@ -1614,7 +1634,7 @@ impl Conversation { _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], pending_save: Task::ready(Ok(())), path: Some(path), - api_key, + credential, buffer, }; this.count_remaining_tokens(cx); @@ -1736,9 +1756,13 @@ impl Conversation { } if should_assist { - let Some(api_key) = self.api_key.borrow().clone() else { - return Default::default(); - }; + let credential = self.credential.borrow().clone(); + match credential { + ProviderCredential::NoCredentials => { + return Default::default(); + } + _ => {} + } let request: Box = Box::new(OpenAIRequest { model: self.model.full_name().to_string(), @@ -1752,7 +1776,7 @@ impl Conversation { temperature: 1.0, }); - let stream = stream_completion(api_key, cx.background().clone(), request); + let stream = stream_completion(credential, cx.background().clone(), request); let assistant_message = self .insert_message_after(last_message_id, Role::Assistant, MessageStatus::Pending, cx) .unwrap(); @@ -2018,57 +2042,62 @@ impl Conversation { fn summarize(&mut self, cx: &mut ModelContext) { if self.message_anchors.len() >= 2 && self.summary.is_none() { - let api_key = self.api_key.borrow().clone(); - if let Some(api_key) = api_key { - let messages = self - .messages(cx) - .take(2) - .map(|message| message.to_open_ai_message(self.buffer.read(cx))) - .chain(Some(RequestMessage { - role: Role::User, - content: - "Summarize the conversation into a short title without punctuation" - .into(), - })); - let request: Box = Box::new(OpenAIRequest { - model: self.model.full_name().to_string(), - messages: messages.collect(), - stream: true, - stop: vec![], - temperature: 1.0, - }); + let credential = self.credential.borrow().clone(); - let stream = stream_completion(api_key, cx.background().clone(), request); - self.pending_summary = cx.spawn(|this, mut cx| { - async move { - let mut messages = stream.await?; - - while let Some(message) = messages.next().await { - let mut message = message?; - if let Some(choice) = message.choices.pop() { - let text = choice.delta.content.unwrap_or_default(); - this.update(&mut cx, |this, cx| { - this.summary - .get_or_insert(Default::default()) - .text - .push_str(&text); - cx.emit(ConversationEvent::SummaryChanged); - }); - } - } - - this.update(&mut cx, |this, cx| { - if let Some(summary) = this.summary.as_mut() { - summary.done = true; - cx.emit(ConversationEvent::SummaryChanged); - } - }); - - anyhow::Ok(()) - } - .log_err() - }); + match credential { + ProviderCredential::NoCredentials => { + return; + } + _ => {} } + + let messages = self + .messages(cx) + .take(2) + .map(|message| message.to_open_ai_message(self.buffer.read(cx))) + .chain(Some(RequestMessage { + role: Role::User, + content: "Summarize the conversation into a short title without punctuation" + .into(), + })); + let request: Box = Box::new(OpenAIRequest { + model: self.model.full_name().to_string(), + messages: messages.collect(), + stream: true, + stop: vec![], + temperature: 1.0, + }); + + let stream = stream_completion(credential, cx.background().clone(), request); + self.pending_summary = cx.spawn(|this, mut cx| { + async move { + let mut messages = stream.await?; + + while let Some(message) = messages.next().await { + let mut message = message?; + if let Some(choice) = message.choices.pop() { + let text = choice.delta.content.unwrap_or_default(); + this.update(&mut cx, |this, cx| { + this.summary + .get_or_insert(Default::default()) + .text + .push_str(&text); + cx.emit(ConversationEvent::SummaryChanged); + }); + } + } + + this.update(&mut cx, |this, cx| { + if let Some(summary) = this.summary.as_mut() { + summary.done = true; + cx.emit(ConversationEvent::SummaryChanged); + } + }); + + anyhow::Ok(()) + } + .log_err() + }); } } @@ -2229,13 +2258,13 @@ struct ConversationEditor { impl ConversationEditor { fn new( - api_key: Rc>>, + credential: Rc>, language_registry: Arc, fs: Arc, workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { - let conversation = cx.add_model(|cx| Conversation::new(api_key, language_registry, cx)); + let conversation = cx.add_model(|cx| Conversation::new(credential, language_registry, cx)); Self::for_conversation(conversation, fs, workspace, cx) } @@ -3431,7 +3460,13 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx)); + let conversation = cx.add_model(|cx| { + Conversation::new( + Rc::new(RefCell::new(ProviderCredential::NotNeeded)), + registry, + cx, + ) + }); let buffer = conversation.read(cx).buffer.clone(); let message_1 = conversation.read(cx).message_anchors[0].clone(); @@ -3559,7 +3594,13 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx)); + let conversation = cx.add_model(|cx| { + Conversation::new( + Rc::new(RefCell::new(ProviderCredential::NotNeeded)), + registry, + cx, + ) + }); let buffer = conversation.read(cx).buffer.clone(); let message_1 = conversation.read(cx).message_anchors[0].clone(); @@ -3655,7 +3696,13 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx)); + let conversation = cx.add_model(|cx| { + Conversation::new( + Rc::new(RefCell::new(ProviderCredential::NotNeeded)), + registry, + cx, + ) + }); let buffer = conversation.read(cx).buffer.clone(); let message_1 = conversation.read(cx).message_anchors[0].clone(); @@ -3737,8 +3784,13 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = - cx.add_model(|cx| Conversation::new(Default::default(), registry.clone(), cx)); + let conversation = cx.add_model(|cx| { + Conversation::new( + Rc::new(RefCell::new(ProviderCredential::NotNeeded)), + registry.clone(), + cx, + ) + }); let buffer = conversation.read(cx).buffer.clone(); let message_0 = conversation.read(cx).message_anchors[0].id; let message_1 = conversation.update(cx, |conversation, cx| { @@ -3775,7 +3827,7 @@ mod tests { Conversation::deserialize( conversation.read(cx).serialize(cx), Default::default(), - Default::default(), + Rc::new(RefCell::new(ProviderCredential::NotNeeded)), registry.clone(), cx, ) From 34747bbbbc9190457eab894f2b86bdae07385312 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sun, 29 Oct 2023 13:47:02 -0500 Subject: [PATCH 16/54] Do not call `scroll_to` twice --- crates/editor/src/editor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index bfb87afff2..701a6882a0 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -967,7 +967,6 @@ impl CompletionsMenu { self.selected_item -= 1; } else { self.selected_item = self.matches.len() - 1; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); } self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.attempt_resolve_selected_completion_documentation(project, cx); @@ -1538,7 +1537,6 @@ impl CodeActionsMenu { self.selected_item -= 1; } else { self.selected_item = self.actions.len() - 1; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); } self.list.scroll_to(ScrollTarget::Show(self.selected_item)); cx.notify(); From dd89b2e6d497d0d4012f880aed8c8ea15177df6e Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sun, 29 Oct 2023 13:54:32 -0500 Subject: [PATCH 17/54] Pull duplicate call out of `if`-`else` block --- crates/editor/src/editor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 701a6882a0..4e449bb7f7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1545,11 +1545,10 @@ impl CodeActionsMenu { fn select_next(&mut self, cx: &mut ViewContext) { if self.selected_item + 1 < self.actions.len() { self.selected_item += 1; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); } else { self.selected_item = 0; - self.list.scroll_to(ScrollTarget::Show(self.selected_item)); } + self.list.scroll_to(ScrollTarget::Show(self.selected_item)); cx.notify(); } From 96bbb5cdea41d537324294bf025cfc6cca7ea51e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 11:14:00 +0200 Subject: [PATCH 18/54] Properly log prettier paths --- crates/prettier/src/prettier.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 79fef40908..53e3101a3b 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -93,7 +93,7 @@ impl Prettier { ) })?; (worktree_root_data.unwrap_or_else(|| { - panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}") + panic!("cannot query prettier for non existing worktree root at {worktree_root:?}") }), None) } else { let full_starting_path = worktree_root.join(&starting_path.starting_path); @@ -106,7 +106,7 @@ impl Prettier { })?; ( worktree_root_data.unwrap_or_else(|| { - panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}") + panic!("cannot query prettier for non existing worktree root at {worktree_root:?}") }), start_path_data, ) From 249bec3cac269909d96226e5732ea36ce8b3569d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 12:13:34 +0200 Subject: [PATCH 19/54] Do not panic on prettier search --- crates/prettier/src/prettier.rs | 120 +++++++++++++++----------------- 1 file changed, 55 insertions(+), 65 deletions(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 53e3101a3b..6784dba7dc 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -81,77 +81,67 @@ impl Prettier { if worktree_root != starting_path.worktree_root_path.as_ref() { vec![worktree_root] } else { - let (worktree_root_metadata, start_path_metadata) = if starting_path - .starting_path - .as_ref() - == Path::new("") - { - let worktree_root_data = - fs.metadata(&worktree_root).await.with_context(|| { - format!( - "FS metadata fetch for worktree root path {worktree_root:?}", - ) - })?; - (worktree_root_data.unwrap_or_else(|| { - panic!("cannot query prettier for non existing worktree root at {worktree_root:?}") - }), None) + let worktree_root_metadata = fs + .metadata(&worktree_root) + .await + .with_context(|| { + format!("FS metadata fetch for worktree root path {worktree_root:?}",) + })? + .with_context(|| { + format!("empty FS metadata for worktree root at {worktree_root:?}") + })?; + if starting_path.starting_path.as_ref() == Path::new("") { + anyhow::ensure!( + !worktree_root_metadata.is_dir, + "For empty start path, worktree root should not be a directory {starting_path:?}" + ); + anyhow::ensure!( + !worktree_root_metadata.is_symlink, + "For empty start path, worktree root should not be a symlink {starting_path:?}" + ); + worktree_root + .parent() + .map(|path| vec![path.to_path_buf()]) + .unwrap_or_default() } else { let full_starting_path = worktree_root.join(&starting_path.starting_path); - let (worktree_root_data, start_path_data) = futures::try_join!( - fs.metadata(&worktree_root), - fs.metadata(&full_starting_path), - ) - .with_context(|| { - format!("FS metadata fetch for starting path {full_starting_path:?}",) - })?; - ( - worktree_root_data.unwrap_or_else(|| { - panic!("cannot query prettier for non existing worktree root at {worktree_root:?}") - }), - start_path_data, - ) - }; + let start_path_metadata = fs + .metadata(&full_starting_path) + .await + .with_context(|| { + format!( + "FS metadata fetch for starting path {full_starting_path:?}" + ) + })? + .with_context(|| { + format!( + "empty FS metadata for starting path {full_starting_path:?}" + ) + })?; - match start_path_metadata { - Some(start_path_metadata) => { - anyhow::ensure!(worktree_root_metadata.is_dir, - "For non-empty start path, worktree root {starting_path:?} should be a directory"); - anyhow::ensure!( - !start_path_metadata.is_dir, - "For non-empty start path, it should not be a directory {starting_path:?}" - ); - anyhow::ensure!( - !start_path_metadata.is_symlink, - "For non-empty start path, it should not be a symlink {starting_path:?}" - ); + anyhow::ensure!(worktree_root_metadata.is_dir, + "For non-empty start path, worktree root {starting_path:?} should be a directory"); + anyhow::ensure!( + !start_path_metadata.is_dir, + "For non-empty start path, it should not be a directory {starting_path:?}" + ); + anyhow::ensure!( + !start_path_metadata.is_symlink, + "For non-empty start path, it should not be a symlink {starting_path:?}" + ); - let file_to_format = starting_path.starting_path.as_ref(); - let mut paths_to_check = VecDeque::from(vec![worktree_root.clone()]); - let mut current_path = worktree_root; - for path_component in file_to_format.components().into_iter() { - current_path = current_path.join(path_component); - paths_to_check.push_front(current_path.clone()); - if path_component.as_os_str().to_string_lossy() == "node_modules" { - break; - } + let file_to_format = starting_path.starting_path.as_ref(); + let mut paths_to_check = VecDeque::from(vec![worktree_root.clone()]); + let mut current_path = worktree_root; + for path_component in file_to_format.components().into_iter() { + current_path = current_path.join(path_component); + paths_to_check.push_front(current_path.clone()); + if path_component.as_os_str().to_string_lossy() == "node_modules" { + break; } - paths_to_check.pop_front(); // last one is the file itself or node_modules, skip it - Vec::from(paths_to_check) - } - None => { - anyhow::ensure!( - !worktree_root_metadata.is_dir, - "For empty start path, worktree root should not be a directory {starting_path:?}" - ); - anyhow::ensure!( - !worktree_root_metadata.is_symlink, - "For empty start path, worktree root should not be a symlink {starting_path:?}" - ); - worktree_root - .parent() - .map(|path| vec![path.to_path_buf()]) - .unwrap_or_default() } + paths_to_check.pop_front(); // last one is the file itself or node_modules, skip it + Vec::from(paths_to_check) } } } From b46a4b56808f7c3521250bef6ee9e4f4389b6973 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 12:07:11 +0200 Subject: [PATCH 20/54] Be more lenient when searching for prettier instance Do not check FS for existence (we'll error when start running prettier), simplify the code for looking it up --- crates/prettier/src/prettier.rs | 62 ++++++--------------------------- 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 6784dba7dc..7517b4ee43 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -67,80 +67,38 @@ impl Prettier { starting_path: Option, fs: Arc, ) -> anyhow::Result { + fn is_node_modules(path_component: &std::path::Component<'_>) -> bool { + path_component.as_os_str().to_string_lossy() == "node_modules" + } + let paths_to_check = match starting_path.as_ref() { Some(starting_path) => { let worktree_root = starting_path .worktree_root_path .components() .into_iter() - .take_while(|path_component| { - path_component.as_os_str().to_string_lossy() != "node_modules" - }) + .take_while(|path_component| !is_node_modules(path_component)) .collect::(); - if worktree_root != starting_path.worktree_root_path.as_ref() { vec![worktree_root] } else { - let worktree_root_metadata = fs - .metadata(&worktree_root) - .await - .with_context(|| { - format!("FS metadata fetch for worktree root path {worktree_root:?}",) - })? - .with_context(|| { - format!("empty FS metadata for worktree root at {worktree_root:?}") - })?; if starting_path.starting_path.as_ref() == Path::new("") { - anyhow::ensure!( - !worktree_root_metadata.is_dir, - "For empty start path, worktree root should not be a directory {starting_path:?}" - ); - anyhow::ensure!( - !worktree_root_metadata.is_symlink, - "For empty start path, worktree root should not be a symlink {starting_path:?}" - ); worktree_root .parent() .map(|path| vec![path.to_path_buf()]) .unwrap_or_default() } else { - let full_starting_path = worktree_root.join(&starting_path.starting_path); - let start_path_metadata = fs - .metadata(&full_starting_path) - .await - .with_context(|| { - format!( - "FS metadata fetch for starting path {full_starting_path:?}" - ) - })? - .with_context(|| { - format!( - "empty FS metadata for starting path {full_starting_path:?}" - ) - })?; - - anyhow::ensure!(worktree_root_metadata.is_dir, - "For non-empty start path, worktree root {starting_path:?} should be a directory"); - anyhow::ensure!( - !start_path_metadata.is_dir, - "For non-empty start path, it should not be a directory {starting_path:?}" - ); - anyhow::ensure!( - !start_path_metadata.is_symlink, - "For non-empty start path, it should not be a symlink {starting_path:?}" - ); - let file_to_format = starting_path.starting_path.as_ref(); - let mut paths_to_check = VecDeque::from(vec![worktree_root.clone()]); + let mut paths_to_check = VecDeque::new(); let mut current_path = worktree_root; for path_component in file_to_format.components().into_iter() { - current_path = current_path.join(path_component); - paths_to_check.push_front(current_path.clone()); - if path_component.as_os_str().to_string_lossy() == "node_modules" { + let new_path = current_path.join(path_component); + let old_path = std::mem::replace(&mut current_path, new_path); + paths_to_check.push_front(old_path); + if is_node_modules(&path_component) { break; } } - paths_to_check.pop_front(); // last one is the file itself or node_modules, skip it Vec::from(paths_to_check) } } From a2c3971ad6202bcec51dc0f36ef13497e94d1597 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 10:02:27 -0400 Subject: [PATCH 21/54] moved authentication for the semantic index into the EmbeddingProvider --- crates/ai/src/auth.rs | 11 +-- crates/ai/src/completion.rs | 18 +--- crates/ai/src/embedding.rs | 15 +--- crates/ai/src/providers/open_ai/auth.rs | 46 ---------- crates/ai/src/providers/open_ai/completion.rs | 78 ++++++++++++---- crates/ai/src/providers/open_ai/embedding.rs | 88 ++++++++++++++----- crates/ai/src/providers/open_ai/mod.rs | 3 +- crates/ai/src/providers/open_ai/new.rs | 11 +++ crates/ai/src/test.rs | 48 +++++----- crates/assistant/src/assistant_panel.rs | 7 +- crates/assistant/src/codegen.rs | 8 +- crates/semantic_index/src/embedding_queue.rs | 17 +--- crates/semantic_index/src/semantic_index.rs | 50 ++++------- .../src/semantic_index_tests.rs | 6 +- 14 files changed, 200 insertions(+), 206 deletions(-) delete mode 100644 crates/ai/src/providers/open_ai/auth.rs create mode 100644 crates/ai/src/providers/open_ai/new.rs diff --git a/crates/ai/src/auth.rs b/crates/ai/src/auth.rs index cb3f2beabb..c6256df216 100644 --- a/crates/ai/src/auth.rs +++ b/crates/ai/src/auth.rs @@ -8,17 +8,8 @@ pub enum ProviderCredential { } pub trait CredentialProvider: Send + Sync { + fn has_credentials(&self) -> bool; fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential; fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential); fn delete_credentials(&self, cx: &AppContext); } - -#[derive(Clone)] -pub struct NullCredentialProvider; -impl CredentialProvider for NullCredentialProvider { - fn retrieve_credentials(&self, _cx: &AppContext) -> ProviderCredential { - ProviderCredential::NotNeeded - } - fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) {} - fn delete_credentials(&self, cx: &AppContext) {} -} diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index 6a2806a5cb..7fdc49e918 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -1,28 +1,14 @@ use anyhow::Result; use futures::{future::BoxFuture, stream::BoxStream}; -use gpui::AppContext; -use crate::{ - auth::{CredentialProvider, ProviderCredential}, - models::LanguageModel, -}; +use crate::{auth::CredentialProvider, models::LanguageModel}; pub trait CompletionRequest: Send + Sync { fn data(&self) -> serde_json::Result; } -pub trait CompletionProvider { +pub trait CompletionProvider: CredentialProvider { fn base_model(&self) -> Box; - fn credential_provider(&self) -> Box; - fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { - self.credential_provider().retrieve_credentials(cx) - } - fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) { - self.credential_provider().save_credentials(cx, credential); - } - fn delete_credentials(&self, cx: &AppContext) { - self.credential_provider().delete_credentials(cx); - } fn complete( &self, prompt: Box, diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index 50f04232ab..6768b7ce7b 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -2,12 +2,11 @@ use std::time::Instant; use anyhow::Result; use async_trait::async_trait; -use gpui::AppContext; use ordered_float::OrderedFloat; use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::ToSql; -use crate::auth::{CredentialProvider, ProviderCredential}; +use crate::auth::CredentialProvider; use crate::models::LanguageModel; #[derive(Debug, PartialEq, Clone)] @@ -70,17 +69,9 @@ impl Embedding { } #[async_trait] -pub trait EmbeddingProvider: Sync + Send { +pub trait EmbeddingProvider: CredentialProvider { fn base_model(&self) -> Box; - fn credential_provider(&self) -> Box; - fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { - self.credential_provider().retrieve_credentials(cx) - } - async fn embed_batch( - &self, - spans: Vec, - credential: ProviderCredential, - ) -> Result>; + async fn embed_batch(&self, spans: Vec) -> Result>; fn max_tokens_per_batch(&self) -> usize; fn rate_limit_expiration(&self) -> Option; } diff --git a/crates/ai/src/providers/open_ai/auth.rs b/crates/ai/src/providers/open_ai/auth.rs deleted file mode 100644 index 7cb51ab449..0000000000 --- a/crates/ai/src/providers/open_ai/auth.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::env; - -use gpui::AppContext; -use util::ResultExt; - -use crate::auth::{CredentialProvider, ProviderCredential}; -use crate::providers::open_ai::OPENAI_API_URL; - -#[derive(Clone)] -pub struct OpenAICredentialProvider {} - -impl CredentialProvider for OpenAICredentialProvider { - fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { - let api_key = if let Ok(api_key) = env::var("OPENAI_API_KEY") { - Some(api_key) - } else if let Some((_, api_key)) = cx - .platform() - .read_credentials(OPENAI_API_URL) - .log_err() - .flatten() - { - String::from_utf8(api_key).log_err() - } else { - None - }; - - if let Some(api_key) = api_key { - ProviderCredential::Credentials { api_key } - } else { - ProviderCredential::NoCredentials - } - } - fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) { - match credential { - ProviderCredential::Credentials { api_key } => { - cx.platform() - .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) - .log_err(); - } - _ => {} - } - } - fn delete_credentials(&self, cx: &AppContext) { - cx.platform().delete_credentials(OPENAI_API_URL).log_err(); - } -} diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs index febe491123..02d25a7eec 100644 --- a/crates/ai/src/providers/open_ai/completion.rs +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -3,14 +3,17 @@ use futures::{ future::BoxFuture, io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, FutureExt, Stream, StreamExt, }; -use gpui::executor::Background; +use gpui::{executor::Background, AppContext}; use isahc::{http::StatusCode, Request, RequestExt}; +use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use std::{ + env, fmt::{self, Display}, io, sync::Arc, }; +use util::ResultExt; use crate::{ auth::{CredentialProvider, ProviderCredential}, @@ -18,9 +21,7 @@ use crate::{ models::LanguageModel, }; -use super::{auth::OpenAICredentialProvider, OpenAILanguageModel}; - -pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; +use crate::providers::open_ai::{OpenAILanguageModel, OPENAI_API_URL}; #[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "lowercase")] @@ -194,42 +195,83 @@ pub async fn stream_completion( pub struct OpenAICompletionProvider { model: OpenAILanguageModel, - credential_provider: OpenAICredentialProvider, - credential: ProviderCredential, + credential: Arc>, executor: Arc, } impl OpenAICompletionProvider { - pub fn new( - model_name: &str, - credential: ProviderCredential, - executor: Arc, - ) -> Self { + pub fn new(model_name: &str, executor: Arc) -> Self { let model = OpenAILanguageModel::load(model_name); - let credential_provider = OpenAICredentialProvider {}; + let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials)); Self { model, - credential_provider, credential, executor, } } } +impl CredentialProvider for OpenAICompletionProvider { + fn has_credentials(&self) -> bool { + match *self.credential.read() { + ProviderCredential::Credentials { .. } => true, + _ => false, + } + } + fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { + let mut credential = self.credential.write(); + match *credential { + ProviderCredential::Credentials { .. } => { + return credential.clone(); + } + _ => { + if let Ok(api_key) = env::var("OPENAI_API_KEY") { + *credential = ProviderCredential::Credentials { api_key }; + } else if let Some((_, api_key)) = cx + .platform() + .read_credentials(OPENAI_API_URL) + .log_err() + .flatten() + { + if let Some(api_key) = String::from_utf8(api_key).log_err() { + *credential = ProviderCredential::Credentials { api_key }; + } + } else { + }; + } + } + + credential.clone() + } + + fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) { + match credential.clone() { + ProviderCredential::Credentials { api_key } => { + cx.platform() + .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) + .log_err(); + } + _ => {} + } + + *self.credential.write() = credential; + } + fn delete_credentials(&self, cx: &AppContext) { + cx.platform().delete_credentials(OPENAI_API_URL).log_err(); + *self.credential.write() = ProviderCredential::NoCredentials; + } +} + impl CompletionProvider for OpenAICompletionProvider { fn base_model(&self) -> Box { let model: Box = Box::new(self.model.clone()); model } - fn credential_provider(&self) -> Box { - let provider: Box = Box::new(self.credential_provider.clone()); - provider - } fn complete( &self, prompt: Box, ) -> BoxFuture<'static, Result>>> { - let credential = self.credential.clone(); + let credential = self.credential.read().clone(); let request = stream_completion(credential, self.executor.clone(), prompt); async move { let response = request.await?; diff --git a/crates/ai/src/providers/open_ai/embedding.rs b/crates/ai/src/providers/open_ai/embedding.rs index dafc94580d..fbfd0028f9 100644 --- a/crates/ai/src/providers/open_ai/embedding.rs +++ b/crates/ai/src/providers/open_ai/embedding.rs @@ -2,27 +2,29 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::AsyncReadExt; use gpui::executor::Background; -use gpui::serde_json; +use gpui::{serde_json, AppContext}; use isahc::http::StatusCode; use isahc::prelude::Configurable; use isahc::{AsyncBody, Response}; use lazy_static::lazy_static; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use parse_duration::parse; use postage::watch; use serde::{Deserialize, Serialize}; +use std::env; use std::ops::Add; use std::sync::Arc; use std::time::{Duration, Instant}; use tiktoken_rs::{cl100k_base, CoreBPE}; use util::http::{HttpClient, Request}; +use util::ResultExt; use crate::auth::{CredentialProvider, ProviderCredential}; use crate::embedding::{Embedding, EmbeddingProvider}; use crate::models::LanguageModel; use crate::providers::open_ai::OpenAILanguageModel; -use crate::providers::open_ai::auth::OpenAICredentialProvider; +use crate::providers::open_ai::OPENAI_API_URL; lazy_static! { static ref OPENAI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap(); @@ -31,7 +33,7 @@ lazy_static! { #[derive(Clone)] pub struct OpenAIEmbeddingProvider { model: OpenAILanguageModel, - credential_provider: OpenAICredentialProvider, + credential: Arc>, pub client: Arc, pub executor: Arc, rate_limit_count_rx: watch::Receiver>, @@ -69,10 +71,11 @@ impl OpenAIEmbeddingProvider { let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx)); let model = OpenAILanguageModel::load("text-embedding-ada-002"); + let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials)); OpenAIEmbeddingProvider { model, - credential_provider: OpenAICredentialProvider {}, + credential, client, executor, rate_limit_count_rx, @@ -80,6 +83,13 @@ impl OpenAIEmbeddingProvider { } } + fn get_api_key(&self) -> Result { + match self.credential.read().clone() { + ProviderCredential::Credentials { api_key } => Ok(api_key), + _ => Err(anyhow!("api credentials not provided")), + } + } + fn resolve_rate_limit(&self) { let reset_time = *self.rate_limit_count_tx.lock().borrow(); @@ -136,6 +146,57 @@ impl OpenAIEmbeddingProvider { } } +impl CredentialProvider for OpenAIEmbeddingProvider { + fn has_credentials(&self) -> bool { + match *self.credential.read() { + ProviderCredential::Credentials { .. } => true, + _ => false, + } + } + fn retrieve_credentials(&self, cx: &AppContext) -> ProviderCredential { + let mut credential = self.credential.write(); + match *credential { + ProviderCredential::Credentials { .. } => { + return credential.clone(); + } + _ => { + if let Ok(api_key) = env::var("OPENAI_API_KEY") { + *credential = ProviderCredential::Credentials { api_key }; + } else if let Some((_, api_key)) = cx + .platform() + .read_credentials(OPENAI_API_URL) + .log_err() + .flatten() + { + if let Some(api_key) = String::from_utf8(api_key).log_err() { + *credential = ProviderCredential::Credentials { api_key }; + } + } else { + }; + } + } + + credential.clone() + } + + fn save_credentials(&self, cx: &AppContext, credential: ProviderCredential) { + match credential.clone() { + ProviderCredential::Credentials { api_key } => { + cx.platform() + .write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) + .log_err(); + } + _ => {} + } + + *self.credential.write() = credential; + } + fn delete_credentials(&self, cx: &AppContext) { + cx.platform().delete_credentials(OPENAI_API_URL).log_err(); + *self.credential.write() = ProviderCredential::NoCredentials; + } +} + #[async_trait] impl EmbeddingProvider for OpenAIEmbeddingProvider { fn base_model(&self) -> Box { @@ -143,12 +204,6 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider { model } - fn credential_provider(&self) -> Box { - let credential_provider: Box = - Box::new(self.credential_provider.clone()); - credential_provider - } - fn max_tokens_per_batch(&self) -> usize { 50000 } @@ -157,18 +212,11 @@ impl EmbeddingProvider for OpenAIEmbeddingProvider { *self.rate_limit_count_rx.borrow() } - async fn embed_batch( - &self, - spans: Vec, - credential: ProviderCredential, - ) -> Result> { + async fn embed_batch(&self, spans: Vec) -> Result> { const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; const MAX_RETRIES: usize = 4; - let api_key = match credential { - ProviderCredential::Credentials { api_key } => anyhow::Ok(api_key), - _ => Err(anyhow!("no api key provided")), - }?; + let api_key = self.get_api_key()?; let mut request_number = 0; let mut rate_limiting = false; diff --git a/crates/ai/src/providers/open_ai/mod.rs b/crates/ai/src/providers/open_ai/mod.rs index 49e29fbc8c..7d2f86045d 100644 --- a/crates/ai/src/providers/open_ai/mod.rs +++ b/crates/ai/src/providers/open_ai/mod.rs @@ -1,4 +1,3 @@ -pub mod auth; pub mod completion; pub mod embedding; pub mod model; @@ -6,3 +5,5 @@ pub mod model; pub use completion::*; pub use embedding::*; pub use model::OpenAILanguageModel; + +pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; diff --git a/crates/ai/src/providers/open_ai/new.rs b/crates/ai/src/providers/open_ai/new.rs new file mode 100644 index 0000000000..c7d67f2ba1 --- /dev/null +++ b/crates/ai/src/providers/open_ai/new.rs @@ -0,0 +1,11 @@ +pub trait LanguageModel { + fn name(&self) -> String; + fn count_tokens(&self, content: &str) -> anyhow::Result; + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result; + fn capacity(&self) -> anyhow::Result; +} diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index b8f99af400..bc9a6a3e43 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -5,10 +5,11 @@ use std::{ use async_trait::async_trait; use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt}; +use gpui::AppContext; use parking_lot::Mutex; use crate::{ - auth::{CredentialProvider, NullCredentialProvider, ProviderCredential}, + auth::{CredentialProvider, ProviderCredential}, completion::{CompletionProvider, CompletionRequest}, embedding::{Embedding, EmbeddingProvider}, models::{LanguageModel, TruncationDirection}, @@ -52,14 +53,12 @@ impl LanguageModel for FakeLanguageModel { pub struct FakeEmbeddingProvider { pub embedding_count: AtomicUsize, - pub credential_provider: NullCredentialProvider, } impl Clone for FakeEmbeddingProvider { fn clone(&self) -> Self { FakeEmbeddingProvider { embedding_count: AtomicUsize::new(self.embedding_count.load(Ordering::SeqCst)), - credential_provider: self.credential_provider.clone(), } } } @@ -68,7 +67,6 @@ impl Default for FakeEmbeddingProvider { fn default() -> Self { FakeEmbeddingProvider { embedding_count: AtomicUsize::default(), - credential_provider: NullCredentialProvider {}, } } } @@ -99,16 +97,22 @@ impl FakeEmbeddingProvider { } } +impl CredentialProvider for FakeEmbeddingProvider { + fn has_credentials(&self) -> bool { + true + } + fn retrieve_credentials(&self, _cx: &AppContext) -> ProviderCredential { + ProviderCredential::NotNeeded + } + fn save_credentials(&self, _cx: &AppContext, _credential: ProviderCredential) {} + fn delete_credentials(&self, _cx: &AppContext) {} +} + #[async_trait] impl EmbeddingProvider for FakeEmbeddingProvider { fn base_model(&self) -> Box { Box::new(FakeLanguageModel { capacity: 1000 }) } - fn credential_provider(&self) -> Box { - let credential_provider: Box = - Box::new(self.credential_provider.clone()); - credential_provider - } fn max_tokens_per_batch(&self) -> usize { 1000 } @@ -117,11 +121,7 @@ impl EmbeddingProvider for FakeEmbeddingProvider { None } - async fn embed_batch( - &self, - spans: Vec, - _credential: ProviderCredential, - ) -> anyhow::Result> { + async fn embed_batch(&self, spans: Vec) -> anyhow::Result> { self.embedding_count .fetch_add(spans.len(), atomic::Ordering::SeqCst); @@ -129,11 +129,11 @@ impl EmbeddingProvider for FakeEmbeddingProvider { } } -pub struct TestCompletionProvider { +pub struct FakeCompletionProvider { last_completion_tx: Mutex>>, } -impl TestCompletionProvider { +impl FakeCompletionProvider { pub fn new() -> Self { Self { last_completion_tx: Mutex::new(None), @@ -150,14 +150,22 @@ impl TestCompletionProvider { } } -impl CompletionProvider for TestCompletionProvider { +impl CredentialProvider for FakeCompletionProvider { + fn has_credentials(&self) -> bool { + true + } + fn retrieve_credentials(&self, _cx: &AppContext) -> ProviderCredential { + ProviderCredential::NotNeeded + } + fn save_credentials(&self, _cx: &AppContext, _credential: ProviderCredential) {} + fn delete_credentials(&self, _cx: &AppContext) {} +} + +impl CompletionProvider for FakeCompletionProvider { fn base_model(&self) -> Box { let model: Box = Box::new(FakeLanguageModel { capacity: 8190 }); model } - fn credential_provider(&self) -> Box { - Box::new(NullCredentialProvider {}) - } fn complete( &self, _prompt: Box, diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index f9187b8785..c10ad2c362 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -10,7 +10,7 @@ use ai::{ auth::ProviderCredential, completion::{CompletionProvider, CompletionRequest}, providers::open_ai::{ - stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, OPENAI_API_URL, + stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, }, }; @@ -48,7 +48,7 @@ use semantic_index::{SemanticIndex, SemanticIndexStatus}; use settings::SettingsStore; use std::{ cell::{Cell, RefCell}, - cmp, env, + cmp, fmt::Write, iter, ops::Range, @@ -210,7 +210,6 @@ impl AssistantPanel { // Defaulting currently to GPT4, allow for this to be set via config. let completion_provider = Box::new(OpenAICompletionProvider::new( "gpt-4", - ProviderCredential::NoCredentials, cx.background().clone(), )); @@ -298,7 +297,6 @@ impl AssistantPanel { cx: &mut ViewContext, project: &ModelHandle, ) { - let credential = self.credential.borrow().clone(); let selection = editor.read(cx).selections.newest_anchor().clone(); if selection.start.excerpt_id() != selection.end.excerpt_id() { return; @@ -330,7 +328,6 @@ impl AssistantPanel { let inline_assist_id = post_inc(&mut self.next_inline_assist_id); let provider = Arc::new(OpenAICompletionProvider::new( "gpt-4", - credential, cx.background().clone(), )); diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index 7f4c95f655..8d8e49902f 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -335,7 +335,7 @@ fn strip_markdown_codeblock( #[cfg(test)] mod tests { use super::*; - use ai::test::TestCompletionProvider; + use ai::test::FakeCompletionProvider; use futures::stream::{self}; use gpui::{executor::Deterministic, TestAppContext}; use indoc::indoc; @@ -379,7 +379,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5)) }); - let provider = Arc::new(TestCompletionProvider::new()); + let provider = Arc::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -445,7 +445,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 6)) }); - let provider = Arc::new(TestCompletionProvider::new()); + let provider = Arc::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -511,7 +511,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 2)) }); - let provider = Arc::new(TestCompletionProvider::new()); + let provider = Arc::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), diff --git a/crates/semantic_index/src/embedding_queue.rs b/crates/semantic_index/src/embedding_queue.rs index 6f792c78e2..6ae8faa4cd 100644 --- a/crates/semantic_index/src/embedding_queue.rs +++ b/crates/semantic_index/src/embedding_queue.rs @@ -1,5 +1,5 @@ use crate::{parsing::Span, JobHandle}; -use ai::{auth::ProviderCredential, embedding::EmbeddingProvider}; +use ai::embedding::EmbeddingProvider; use gpui::executor::Background; use parking_lot::Mutex; use smol::channel; @@ -41,7 +41,6 @@ pub struct EmbeddingQueue { pending_batch_token_count: usize, finished_files_tx: channel::Sender, finished_files_rx: channel::Receiver, - pub provider_credential: ProviderCredential, } #[derive(Clone)] @@ -51,11 +50,7 @@ pub struct FileFragmentToEmbed { } impl EmbeddingQueue { - pub fn new( - embedding_provider: Arc, - executor: Arc, - provider_credential: ProviderCredential, - ) -> Self { + pub fn new(embedding_provider: Arc, executor: Arc) -> Self { let (finished_files_tx, finished_files_rx) = channel::unbounded(); Self { embedding_provider, @@ -64,14 +59,9 @@ impl EmbeddingQueue { pending_batch_token_count: 0, finished_files_tx, finished_files_rx, - provider_credential, } } - pub fn set_credential(&mut self, credential: ProviderCredential) { - self.provider_credential = credential; - } - pub fn push(&mut self, file: FileToEmbed) { if file.spans.is_empty() { self.finished_files_tx.try_send(file).unwrap(); @@ -118,7 +108,6 @@ impl EmbeddingQueue { let finished_files_tx = self.finished_files_tx.clone(); let embedding_provider = self.embedding_provider.clone(); - let credential = self.provider_credential.clone(); self.executor .spawn(async move { @@ -143,7 +132,7 @@ impl EmbeddingQueue { return; }; - match embedding_provider.embed_batch(spans, credential).await { + match embedding_provider.embed_batch(spans).await { Ok(embeddings) => { let mut embeddings = embeddings.into_iter(); for fragment in batch { diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 7fb5f749b4..818faa0444 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -7,7 +7,6 @@ pub mod semantic_index_settings; mod semantic_index_tests; use crate::semantic_index_settings::SemanticIndexSettings; -use ai::auth::ProviderCredential; use ai::embedding::{Embedding, EmbeddingProvider}; use ai::providers::open_ai::OpenAIEmbeddingProvider; use anyhow::{anyhow, Result}; @@ -125,8 +124,6 @@ pub struct SemanticIndex { _embedding_task: Task<()>, _parsing_files_tasks: Vec>, projects: HashMap, ProjectState>, - provider_credential: ProviderCredential, - embedding_queue: Arc>, } struct ProjectState { @@ -281,24 +278,17 @@ impl SemanticIndex { } pub fn authenticate(&mut self, cx: &AppContext) -> bool { - let existing_credential = self.provider_credential.clone(); - let credential = match existing_credential { - ProviderCredential::NoCredentials => self.embedding_provider.retrieve_credentials(cx), - _ => existing_credential, - }; + if !self.embedding_provider.has_credentials() { + self.embedding_provider.retrieve_credentials(cx); + } else { + return true; + } - self.provider_credential = credential.clone(); - self.embedding_queue.lock().set_credential(credential); - self.is_authenticated() + self.embedding_provider.has_credentials() } pub fn is_authenticated(&self) -> bool { - let credential = &self.provider_credential; - match credential { - &ProviderCredential::Credentials { .. } => true, - &ProviderCredential::NotNeeded => true, - _ => false, - } + self.embedding_provider.has_credentials() } pub fn enabled(cx: &AppContext) -> bool { @@ -348,7 +338,7 @@ impl SemanticIndex { Ok(cx.add_model(|cx| { let t0 = Instant::now(); let embedding_queue = - EmbeddingQueue::new(embedding_provider.clone(), cx.background().clone(), ProviderCredential::NoCredentials); + EmbeddingQueue::new(embedding_provider.clone(), cx.background().clone()); let _embedding_task = cx.background().spawn({ let embedded_files = embedding_queue.finished_files(); let db = db.clone(); @@ -413,8 +403,6 @@ impl SemanticIndex { _embedding_task, _parsing_files_tasks, projects: Default::default(), - provider_credential: ProviderCredential::NoCredentials, - embedding_queue } })) } @@ -729,14 +717,13 @@ impl SemanticIndex { let index = self.index_project(project.clone(), cx); let embedding_provider = self.embedding_provider.clone(); - let credential = self.provider_credential.clone(); cx.spawn(|this, mut cx| async move { index.await?; let t0 = Instant::now(); let query = embedding_provider - .embed_batch(vec![query], credential) + .embed_batch(vec![query]) .await? .pop() .ok_or_else(|| anyhow!("could not embed query"))?; @@ -954,7 +941,6 @@ impl SemanticIndex { let fs = self.fs.clone(); let db_path = self.db.path().clone(); let background = cx.background().clone(); - let credential = self.provider_credential.clone(); cx.background().spawn(async move { let db = VectorDatabase::new(fs, db_path.clone(), background).await?; let mut results = Vec::::new(); @@ -969,15 +955,10 @@ impl SemanticIndex { .parse_file_with_template(None, &snapshot.text(), language) .log_err() .unwrap_or_default(); - if Self::embed_spans( - &mut spans, - embedding_provider.as_ref(), - &db, - credential.clone(), - ) - .await - .log_err() - .is_some() + if Self::embed_spans(&mut spans, embedding_provider.as_ref(), &db) + .await + .log_err() + .is_some() { for span in spans { let similarity = span.embedding.unwrap().similarity(&query); @@ -1201,7 +1182,6 @@ impl SemanticIndex { spans: &mut [Span], embedding_provider: &dyn EmbeddingProvider, db: &VectorDatabase, - credential: ProviderCredential, ) -> Result<()> { let mut batch = Vec::new(); let mut batch_tokens = 0; @@ -1224,7 +1204,7 @@ impl SemanticIndex { if batch_tokens + span.token_count > embedding_provider.max_tokens_per_batch() { let batch_embeddings = embedding_provider - .embed_batch(mem::take(&mut batch), credential.clone()) + .embed_batch(mem::take(&mut batch)) .await?; embeddings.extend(batch_embeddings); batch_tokens = 0; @@ -1236,7 +1216,7 @@ impl SemanticIndex { if !batch.is_empty() { let batch_embeddings = embedding_provider - .embed_batch(mem::take(&mut batch), credential) + .embed_batch(mem::take(&mut batch)) .await?; embeddings.extend(batch_embeddings); diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 7d5a4e22e8..7a91d1e100 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -220,11 +220,7 @@ async fn test_embedding_batching(cx: &mut TestAppContext, mut rng: StdRng) { let embedding_provider = Arc::new(FakeEmbeddingProvider::default()); - let mut queue = EmbeddingQueue::new( - embedding_provider.clone(), - cx.background(), - ai::auth::ProviderCredential::NoCredentials, - ); + let mut queue = EmbeddingQueue::new(embedding_provider.clone(), cx.background()); for file in &files { queue.push(file.clone()); } From 61cc3b93e3966d453e6405153d4e3fabac49524d Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 10:40:46 -0400 Subject: [PATCH 22/54] Factor out `ThemePrinter` into its own module --- crates/theme_converter/src/main.rs | 174 +------------------- crates/theme_converter/src/theme_printer.rs | 174 ++++++++++++++++++++ 2 files changed, 181 insertions(+), 167 deletions(-) create mode 100644 crates/theme_converter/src/theme_printer.rs diff --git a/crates/theme_converter/src/main.rs b/crates/theme_converter/src/main.rs index 33a939d525..ec31296c1d 100644 --- a/crates/theme_converter/src/main.rs +++ b/crates/theme_converter/src/main.rs @@ -1,16 +1,20 @@ +mod theme_printer; + use std::borrow::Cow; use std::collections::HashMap; use std::fmt::{self, Debug}; use anyhow::{anyhow, Context, Result}; use clap::Parser; -use gpui2::{hsla, rgb, serde_json, AssetSource, Hsla, Rgba, SharedString}; +use gpui2::{hsla, rgb, serde_json, AssetSource, Hsla, SharedString}; use log::LevelFilter; use rust_embed::RustEmbed; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; use simplelog::SimpleLogger; -use theme2::{PlayerTheme, SyntaxTheme, ThemeMetadata}; +use theme2::{PlayerTheme, SyntaxTheme}; + +use crate::theme_printer::ThemePrinter; #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -28,7 +32,7 @@ fn main() -> Result<()> { let theme = convert_theme(json_theme, legacy_theme)?; - println!("{:#?}", ThemePrinter(theme)); + println!("{:#?}", ThemePrinter::new(theme)); Ok(()) } @@ -321,167 +325,3 @@ where } deserializer.deserialize_map(SyntaxVisitor) } - -pub struct ThemePrinter(theme2::Theme); - -struct HslaPrinter(Hsla); - -impl Debug for HslaPrinter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", IntoPrinter(&Rgba::from(self.0))) - } -} - -struct IntoPrinter<'a, D: Debug>(&'a D); - -impl<'a, D: Debug> Debug for IntoPrinter<'a, D> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}.into()", self.0) - } -} - -impl Debug for ThemePrinter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Theme") - .field("metadata", &ThemeMetadataPrinter(self.0.metadata.clone())) - .field("transparent", &HslaPrinter(self.0.transparent)) - .field( - "mac_os_traffic_light_red", - &HslaPrinter(self.0.mac_os_traffic_light_red), - ) - .field( - "mac_os_traffic_light_yellow", - &HslaPrinter(self.0.mac_os_traffic_light_yellow), - ) - .field( - "mac_os_traffic_light_green", - &HslaPrinter(self.0.mac_os_traffic_light_green), - ) - .field("border", &HslaPrinter(self.0.border)) - .field("border_variant", &HslaPrinter(self.0.border_variant)) - .field("border_focused", &HslaPrinter(self.0.border_focused)) - .field( - "border_transparent", - &HslaPrinter(self.0.border_transparent), - ) - .field("elevated_surface", &HslaPrinter(self.0.elevated_surface)) - .field("surface", &HslaPrinter(self.0.surface)) - .field("background", &HslaPrinter(self.0.background)) - .field("filled_element", &HslaPrinter(self.0.filled_element)) - .field( - "filled_element_hover", - &HslaPrinter(self.0.filled_element_hover), - ) - .field( - "filled_element_active", - &HslaPrinter(self.0.filled_element_active), - ) - .field( - "filled_element_selected", - &HslaPrinter(self.0.filled_element_selected), - ) - .field( - "filled_element_disabled", - &HslaPrinter(self.0.filled_element_disabled), - ) - .field("ghost_element", &HslaPrinter(self.0.ghost_element)) - .field( - "ghost_element_hover", - &HslaPrinter(self.0.ghost_element_hover), - ) - .field( - "ghost_element_active", - &HslaPrinter(self.0.ghost_element_active), - ) - .field( - "ghost_element_selected", - &HslaPrinter(self.0.ghost_element_selected), - ) - .field( - "ghost_element_disabled", - &HslaPrinter(self.0.ghost_element_disabled), - ) - .field("text", &HslaPrinter(self.0.text)) - .field("text_muted", &HslaPrinter(self.0.text_muted)) - .field("text_placeholder", &HslaPrinter(self.0.text_placeholder)) - .field("text_disabled", &HslaPrinter(self.0.text_disabled)) - .field("text_accent", &HslaPrinter(self.0.text_accent)) - .field("icon_muted", &HslaPrinter(self.0.icon_muted)) - .field("syntax", &SyntaxThemePrinter(self.0.syntax.clone())) - .field("status_bar", &HslaPrinter(self.0.status_bar)) - .field("title_bar", &HslaPrinter(self.0.title_bar)) - .field("toolbar", &HslaPrinter(self.0.toolbar)) - .field("tab_bar", &HslaPrinter(self.0.tab_bar)) - .field("editor", &HslaPrinter(self.0.editor)) - .field("editor_subheader", &HslaPrinter(self.0.editor_subheader)) - .field( - "editor_active_line", - &HslaPrinter(self.0.editor_active_line), - ) - .field("terminal", &HslaPrinter(self.0.terminal)) - .field( - "image_fallback_background", - &HslaPrinter(self.0.image_fallback_background), - ) - .field("git_created", &HslaPrinter(self.0.git_created)) - .field("git_modified", &HslaPrinter(self.0.git_modified)) - .field("git_deleted", &HslaPrinter(self.0.git_deleted)) - .field("git_conflict", &HslaPrinter(self.0.git_conflict)) - .field("git_ignored", &HslaPrinter(self.0.git_ignored)) - .field("git_renamed", &HslaPrinter(self.0.git_renamed)) - .field("players", &self.0.players.map(PlayerThemePrinter)) - .finish() - } -} - -pub struct ThemeMetadataPrinter(ThemeMetadata); - -impl Debug for ThemeMetadataPrinter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ThemeMetadata") - .field("name", &IntoPrinter(&self.0.name)) - .field("is_light", &self.0.is_light) - .finish() - } -} - -pub struct SyntaxThemePrinter(SyntaxTheme); - -impl Debug for SyntaxThemePrinter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SyntaxTheme") - .field( - "highlights", - &VecPrinter( - &self - .0 - .highlights - .iter() - .map(|(token, highlight)| { - (IntoPrinter(token), HslaPrinter(highlight.color.unwrap())) - }) - .collect(), - ), - ) - .finish() - } -} - -pub struct VecPrinter<'a, T>(&'a Vec); - -impl<'a, T: Debug> Debug for VecPrinter<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "vec!{:?}", &self.0) - } -} - -pub struct PlayerThemePrinter(PlayerTheme); - -impl Debug for PlayerThemePrinter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PlayerTheme") - .field("cursor", &HslaPrinter(self.0.cursor)) - .field("selection", &HslaPrinter(self.0.selection)) - .finish() - } -} diff --git a/crates/theme_converter/src/theme_printer.rs b/crates/theme_converter/src/theme_printer.rs new file mode 100644 index 0000000000..3a9bdb159b --- /dev/null +++ b/crates/theme_converter/src/theme_printer.rs @@ -0,0 +1,174 @@ +use std::fmt::{self, Debug}; + +use gpui2::{Hsla, Rgba}; +use theme2::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub struct ThemePrinter(Theme); + +impl ThemePrinter { + pub fn new(theme: Theme) -> Self { + Self(theme) + } +} + +struct HslaPrinter(Hsla); + +impl Debug for HslaPrinter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", IntoPrinter(&Rgba::from(self.0))) + } +} + +struct IntoPrinter<'a, D: Debug>(&'a D); + +impl<'a, D: Debug> Debug for IntoPrinter<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}.into()", self.0) + } +} + +impl Debug for ThemePrinter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Theme") + .field("metadata", &ThemeMetadataPrinter(self.0.metadata.clone())) + .field("transparent", &HslaPrinter(self.0.transparent)) + .field( + "mac_os_traffic_light_red", + &HslaPrinter(self.0.mac_os_traffic_light_red), + ) + .field( + "mac_os_traffic_light_yellow", + &HslaPrinter(self.0.mac_os_traffic_light_yellow), + ) + .field( + "mac_os_traffic_light_green", + &HslaPrinter(self.0.mac_os_traffic_light_green), + ) + .field("border", &HslaPrinter(self.0.border)) + .field("border_variant", &HslaPrinter(self.0.border_variant)) + .field("border_focused", &HslaPrinter(self.0.border_focused)) + .field( + "border_transparent", + &HslaPrinter(self.0.border_transparent), + ) + .field("elevated_surface", &HslaPrinter(self.0.elevated_surface)) + .field("surface", &HslaPrinter(self.0.surface)) + .field("background", &HslaPrinter(self.0.background)) + .field("filled_element", &HslaPrinter(self.0.filled_element)) + .field( + "filled_element_hover", + &HslaPrinter(self.0.filled_element_hover), + ) + .field( + "filled_element_active", + &HslaPrinter(self.0.filled_element_active), + ) + .field( + "filled_element_selected", + &HslaPrinter(self.0.filled_element_selected), + ) + .field( + "filled_element_disabled", + &HslaPrinter(self.0.filled_element_disabled), + ) + .field("ghost_element", &HslaPrinter(self.0.ghost_element)) + .field( + "ghost_element_hover", + &HslaPrinter(self.0.ghost_element_hover), + ) + .field( + "ghost_element_active", + &HslaPrinter(self.0.ghost_element_active), + ) + .field( + "ghost_element_selected", + &HslaPrinter(self.0.ghost_element_selected), + ) + .field( + "ghost_element_disabled", + &HslaPrinter(self.0.ghost_element_disabled), + ) + .field("text", &HslaPrinter(self.0.text)) + .field("text_muted", &HslaPrinter(self.0.text_muted)) + .field("text_placeholder", &HslaPrinter(self.0.text_placeholder)) + .field("text_disabled", &HslaPrinter(self.0.text_disabled)) + .field("text_accent", &HslaPrinter(self.0.text_accent)) + .field("icon_muted", &HslaPrinter(self.0.icon_muted)) + .field("syntax", &SyntaxThemePrinter(self.0.syntax.clone())) + .field("status_bar", &HslaPrinter(self.0.status_bar)) + .field("title_bar", &HslaPrinter(self.0.title_bar)) + .field("toolbar", &HslaPrinter(self.0.toolbar)) + .field("tab_bar", &HslaPrinter(self.0.tab_bar)) + .field("editor", &HslaPrinter(self.0.editor)) + .field("editor_subheader", &HslaPrinter(self.0.editor_subheader)) + .field( + "editor_active_line", + &HslaPrinter(self.0.editor_active_line), + ) + .field("terminal", &HslaPrinter(self.0.terminal)) + .field( + "image_fallback_background", + &HslaPrinter(self.0.image_fallback_background), + ) + .field("git_created", &HslaPrinter(self.0.git_created)) + .field("git_modified", &HslaPrinter(self.0.git_modified)) + .field("git_deleted", &HslaPrinter(self.0.git_deleted)) + .field("git_conflict", &HslaPrinter(self.0.git_conflict)) + .field("git_ignored", &HslaPrinter(self.0.git_ignored)) + .field("git_renamed", &HslaPrinter(self.0.git_renamed)) + .field("players", &self.0.players.map(PlayerThemePrinter)) + .finish() + } +} + +pub struct ThemeMetadataPrinter(ThemeMetadata); + +impl Debug for ThemeMetadataPrinter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ThemeMetadata") + .field("name", &IntoPrinter(&self.0.name)) + .field("is_light", &self.0.is_light) + .finish() + } +} + +pub struct SyntaxThemePrinter(SyntaxTheme); + +impl Debug for SyntaxThemePrinter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SyntaxTheme") + .field( + "highlights", + &VecPrinter( + &self + .0 + .highlights + .iter() + .map(|(token, highlight)| { + (IntoPrinter(token), HslaPrinter(highlight.color.unwrap())) + }) + .collect(), + ), + ) + .finish() + } +} + +pub struct VecPrinter<'a, T>(&'a Vec); + +impl<'a, T: Debug> Debug for VecPrinter<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "vec!{:?}", &self.0) + } +} + +pub struct PlayerThemePrinter(PlayerTheme); + +impl Debug for PlayerThemePrinter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PlayerTheme") + .field("cursor", &HslaPrinter(self.0.cursor)) + .field("selection", &HslaPrinter(self.0.selection)) + .finish() + } +} From 3591ffe4a799a73cddc1242bdeb6a7eb68e51e71 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 11:04:03 -0400 Subject: [PATCH 23/54] Emit all themes at once --- Cargo.lock | 1 + crates/theme2/src/themes/one_dark.rs | 212 ++++++++++--------------- crates/theme2/src/themes/sandcastle.rs | 211 ++++++++++-------------- crates/theme_converter/Cargo.toml | 1 + crates/theme_converter/src/main.rs | 37 ++++- 5 files changed, 198 insertions(+), 264 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e158405b7..dc5684e115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8740,6 +8740,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 4.4.4", + "convert_case 0.6.0", "gpui2", "log", "rust-embed", diff --git a/crates/theme2/src/themes/one_dark.rs b/crates/theme2/src/themes/one_dark.rs index c59f4da16a..d9be9043d6 100644 --- a/crates/theme2/src/themes/one_dark.rs +++ b/crates/theme2/src/themes/one_dark.rs @@ -1,131 +1,85 @@ -use gpui2::rgba; - -use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; - -pub fn one_dark() -> Theme { - Theme { - metadata: ThemeMetadata { - name: "One Dark".into(), - is_light: false, +Theme { + metadata: ThemeMetadata { + name: "One Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x464b57ff).into(), + border_variant: rgba(0x464b57ff).into(), + border_focused: rgba(0x293b5bff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x3b414dff).into(), + surface: rgba(0x2f343eff).into(), + background: rgba(0x3b414dff).into(), + filled_element: rgba(0x3b414dff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x18243dff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x18243dff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xc8ccd4ff).into(), + text_muted: rgba(0x838994ff).into(), + text_placeholder: rgba(0xd07277ff).into(), + text_disabled: rgba(0x555a63ff).into(), + text_accent: rgba(0x74ade8ff).into(), + icon_muted: rgba(0x838994ff).into(), + syntax: SyntaxTheme { + highlights: vec![("punctuation".into(), rgba(0xacb2beff).into()), ("comment".into(), rgba(0x5d636fff).into()), ("predictive".into(), rgba(0x5a6a87ff).into()), ("comment.doc".into(), rgba(0x878e98ff).into()), ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), ("constant".into(), rgba(0xdfc184ff).into()), ("text.literal".into(), rgba(0xa1c181ff).into()), ("attribute".into(), rgba(0x74ade8ff).into()), ("link_text".into(), rgba(0x73ade9ff).into()), ("primary".into(), rgba(0xacb2beff).into()), ("enum".into(), rgba(0xd07277ff).into()), ("emphasis.strong".into(), rgba(0xbf956aff).into()), ("property".into(), rgba(0xd07277ff).into()), ("emphasis".into(), rgba(0x74ade8ff).into()), ("string.regex".into(), rgba(0xbf956aff).into()), ("variable".into(), rgba(0xc8ccd4ff).into()), ("keyword".into(), rgba(0xb477cfff).into()), ("boolean".into(), rgba(0xbf956aff).into()), ("variable.special".into(), rgba(0xbf956aff).into()), ("punctuation.special".into(), rgba(0xb1574bff).into()), ("title".into(), rgba(0xd07277ff).into()), ("punctuation.list_marker".into(), rgba(0xd07277ff).into()), ("string.special".into(), rgba(0xbf956aff).into()), ("number".into(), rgba(0xbf956aff).into()), ("preproc".into(), rgba(0xc8ccd4ff).into()), ("constructor".into(), rgba(0x73ade9ff).into()), ("string.special.symbol".into(), rgba(0xbf956aff).into()), ("link_uri".into(), rgba(0x6eb4bfff).into()), ("string".into(), rgba(0xa1c181ff).into()), ("string.escape".into(), rgba(0x878e98ff).into()), ("operator".into(), rgba(0x6eb4bfff).into()), ("hint".into(), rgba(0x5a6f89ff).into()), ("label".into(), rgba(0x74ade8ff).into()), ("type".into(), rgba(0x6eb4bfff).into()), ("embedded".into(), rgba(0xc8ccd4ff).into()), ("function".into(), rgba(0x73ade9ff).into()), ("tag".into(), rgba(0x74ade8ff).into()), ("variant".into(), rgba(0x73ade9ff).into()), ("punctuation.delimiter".into(), rgba(0xb2b9c6ff).into())], + }, + status_bar: rgba(0x3b414dff).into(), + title_bar: rgba(0x3b414dff).into(), + toolbar: rgba(0x282c33ff).into(), + tab_bar: rgba(0x2f343eff).into(), + editor: rgba(0x282c33ff).into(), + editor_subheader: rgba(0x2f343eff).into(), + editor_active_line: rgba(0x2f343eff).into(), + terminal: rgba(0x282c33ff).into(), + image_fallback_background: rgba(0x3b414dff).into(), + git_created: rgba(0xa1c181ff).into(), + git_modified: rgba(0x74ade8ff).into(), + git_deleted: rgba(0xd07277ff).into(), + git_conflict: rgba(0xdec184ff).into(), + git_ignored: rgba(0x555a63ff).into(), + git_renamed: rgba(0xdec184ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x74ade8ff).into(), + selection: rgba(0x74ade83d).into(), }, - transparent: rgba(0x00000000).into(), - mac_os_traffic_light_red: rgba(0xec695eff).into(), - mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), - mac_os_traffic_light_green: rgba(0x61c553ff).into(), - border: rgba(0x464b57ff).into(), - border_variant: rgba(0x464b57ff).into(), - border_focused: rgba(0x293b5bff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0x3b414dff).into(), - surface: rgba(0x2f343eff).into(), - background: rgba(0x3b414dff).into(), - filled_element: rgba(0x3b414dff).into(), - filled_element_hover: rgba(0xffffff1e).into(), - filled_element_active: rgba(0xffffff28).into(), - filled_element_selected: rgba(0x18243dff).into(), - filled_element_disabled: rgba(0x00000000).into(), - ghost_element: rgba(0x00000000).into(), - ghost_element_hover: rgba(0xffffff14).into(), - ghost_element_active: rgba(0xffffff1e).into(), - ghost_element_selected: rgba(0x18243dff).into(), - ghost_element_disabled: rgba(0x00000000).into(), - text: rgba(0xc8ccd4ff).into(), - text_muted: rgba(0x838994ff).into(), - text_placeholder: rgba(0xd07277ff).into(), - text_disabled: rgba(0x555a63ff).into(), - text_accent: rgba(0x74ade8ff).into(), - icon_muted: rgba(0x838994ff).into(), - syntax: SyntaxTheme { - highlights: vec![ - ("link_uri".into(), rgba(0x6eb4bfff).into()), - ("number".into(), rgba(0xbf956aff).into()), - ("property".into(), rgba(0xd07277ff).into()), - ("boolean".into(), rgba(0xbf956aff).into()), - ("label".into(), rgba(0x74ade8ff).into()), - ("punctuation.list_marker".into(), rgba(0xd07277ff).into()), - ("keyword".into(), rgba(0xb477cfff).into()), - ("punctuation.delimiter".into(), rgba(0xb2b9c6ff).into()), - ("string.special".into(), rgba(0xbf956aff).into()), - ("constant".into(), rgba(0xdfc184ff).into()), - ("punctuation".into(), rgba(0xacb2beff).into()), - ("variable.special".into(), rgba(0xbf956aff).into()), - ("preproc".into(), rgba(0xc8ccd4ff).into()), - ("enum".into(), rgba(0xd07277ff).into()), - ("attribute".into(), rgba(0x74ade8ff).into()), - ("emphasis.strong".into(), rgba(0xbf956aff).into()), - ("title".into(), rgba(0xd07277ff).into()), - ("hint".into(), rgba(0x5a6f89ff).into()), - ("emphasis".into(), rgba(0x74ade8ff).into()), - ("string.regex".into(), rgba(0xbf956aff).into()), - ("link_text".into(), rgba(0x73ade9ff).into()), - ("string".into(), rgba(0xa1c181ff).into()), - ("comment.doc".into(), rgba(0x878e98ff).into()), - ("punctuation.special".into(), rgba(0xb1574bff).into()), - ("primary".into(), rgba(0xacb2beff).into()), - ("operator".into(), rgba(0x6eb4bfff).into()), - ("function".into(), rgba(0x73ade9ff).into()), - ("string.special.symbol".into(), rgba(0xbf956aff).into()), - ("type".into(), rgba(0x6eb4bfff).into()), - ("variant".into(), rgba(0x73ade9ff).into()), - ("tag".into(), rgba(0x74ade8ff).into()), - ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), - ("embedded".into(), rgba(0xc8ccd4ff).into()), - ("string.escape".into(), rgba(0x878e98ff).into()), - ("variable".into(), rgba(0xc8ccd4ff).into()), - ("predictive".into(), rgba(0x5a6a87ff).into()), - ("comment".into(), rgba(0x5d636fff).into()), - ("text.literal".into(), rgba(0xa1c181ff).into()), - ("constructor".into(), rgba(0x73ade9ff).into()), - ], + PlayerTheme { + cursor: rgba(0xa1c181ff).into(), + selection: rgba(0xa1c1813d).into(), }, - status_bar: rgba(0x3b414dff).into(), - title_bar: rgba(0x3b414dff).into(), - toolbar: rgba(0x282c33ff).into(), - tab_bar: rgba(0x2f343eff).into(), - editor: rgba(0x282c33ff).into(), - editor_subheader: rgba(0x2f343eff).into(), - editor_active_line: rgba(0x2f343eff).into(), - terminal: rgba(0x282c33ff).into(), - image_fallback_background: rgba(0x3b414dff).into(), - git_created: rgba(0xa1c181ff).into(), - git_modified: rgba(0x74ade8ff).into(), - git_deleted: rgba(0xd07277ff).into(), - git_conflict: rgba(0xdec184ff).into(), - git_ignored: rgba(0x555a63ff).into(), - git_renamed: rgba(0xdec184ff).into(), - players: [ - PlayerTheme { - cursor: rgba(0x74ade8ff).into(), - selection: rgba(0x74ade83d).into(), - }, - PlayerTheme { - cursor: rgba(0xa1c181ff).into(), - selection: rgba(0xa1c1813d).into(), - }, - PlayerTheme { - cursor: rgba(0xbe5046ff).into(), - selection: rgba(0xbe50463d).into(), - }, - PlayerTheme { - cursor: rgba(0xbf956aff).into(), - selection: rgba(0xbf956a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xb477cfff).into(), - selection: rgba(0xb477cf3d).into(), - }, - PlayerTheme { - cursor: rgba(0x6eb4bfff).into(), - selection: rgba(0x6eb4bf3d).into(), - }, - PlayerTheme { - cursor: rgba(0xd07277ff).into(), - selection: rgba(0xd072773d).into(), - }, - PlayerTheme { - cursor: rgba(0xdec184ff).into(), - selection: rgba(0xdec1843d).into(), - }, - ], - } -} + PlayerTheme { + cursor: rgba(0xbe5046ff).into(), + selection: rgba(0xbe50463d).into(), + }, + PlayerTheme { + cursor: rgba(0xbf956aff).into(), + selection: rgba(0xbf956a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xb477cfff).into(), + selection: rgba(0xb477cf3d).into(), + }, + PlayerTheme { + cursor: rgba(0x6eb4bfff).into(), + selection: rgba(0x6eb4bf3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd07277ff).into(), + selection: rgba(0xd072773d).into(), + }, + PlayerTheme { + cursor: rgba(0xdec184ff).into(), + selection: rgba(0xdec1843d).into(), + }, + ], +} \ No newline at end of file diff --git a/crates/theme2/src/themes/sandcastle.rs b/crates/theme2/src/themes/sandcastle.rs index 4e87c427f0..507c99209f 100644 --- a/crates/theme2/src/themes/sandcastle.rs +++ b/crates/theme2/src/themes/sandcastle.rs @@ -1,130 +1,85 @@ -use gpui2::rgba; - -use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; - -pub fn sandcastle() -> Theme { - Theme { - metadata: ThemeMetadata { - name: "Sandcastle".into(), - is_light: false, +Theme { + metadata: ThemeMetadata { + name: "Sandcastle".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x3d4350ff).into(), + border_variant: rgba(0x3d4350ff).into(), + border_focused: rgba(0x223131ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x333944ff).into(), + surface: rgba(0x2b3038ff).into(), + background: rgba(0x333944ff).into(), + filled_element: rgba(0x333944ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x171e1eff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x171e1eff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfdf4c1ff).into(), + text_muted: rgba(0xa69782ff).into(), + text_placeholder: rgba(0xb3627aff).into(), + text_disabled: rgba(0x827568ff).into(), + text_accent: rgba(0x518b8bff).into(), + icon_muted: rgba(0xa69782ff).into(), + syntax: SyntaxTheme { + highlights: vec![("variable".into(), rgba(0xfdf4c1ff).into()), ("comment.doc".into(), rgba(0xa89984ff).into()), ("punctuation".into(), rgba(0xd5c5a1ff).into()), ("comment".into(), rgba(0xa89984ff).into()), ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), ("string.regex".into(), rgba(0xa07d3aff).into()), ("emphasis".into(), rgba(0x518b8bff).into()), ("link_uri".into(), rgba(0x83a598ff).into()), ("label".into(), rgba(0x518b8bff).into()), ("property".into(), rgba(0x518b8bff).into()), ("constant".into(), rgba(0x83a598ff).into()), ("enum".into(), rgba(0xa07d3aff).into()), ("number".into(), rgba(0x83a598ff).into()), ("link_text".into(), rgba(0xa07d3aff).into()), ("hint".into(), rgba(0x727d68ff).into()), ("type".into(), rgba(0x83a598ff).into()), ("title".into(), rgba(0xfdf4c1ff).into()), ("punctuation.delimiter".into(), rgba(0xd5c5a1ff).into()), ("function".into(), rgba(0xa07d3aff).into()), ("preproc".into(), rgba(0xfdf4c1ff).into()), ("boolean".into(), rgba(0x83a598ff).into()), ("keyword".into(), rgba(0x518b8bff).into()), ("attribute".into(), rgba(0x518b8bff).into()), ("predictive".into(), rgba(0x5c6152ff).into()), ("string".into(), rgba(0xa07d3aff).into()), ("emphasis.strong".into(), rgba(0x518b8bff).into()), ("punctuation.bracket".into(), rgba(0xd5c5a1ff).into()), ("string.special.symbol".into(), rgba(0xa07d3aff).into()), ("variant".into(), rgba(0x518b8bff).into()), ("tag".into(), rgba(0x518b8bff).into()), ("primary".into(), rgba(0xfdf4c1ff).into()), ("string.special".into(), rgba(0xa07d3aff).into()), ("punctuation.list_marker".into(), rgba(0xd5c5a1ff).into()), ("string.escape".into(), rgba(0xa89984ff).into()), ("constructor".into(), rgba(0x518b8bff).into()), ("embedded".into(), rgba(0xfdf4c1ff).into()), ("operator".into(), rgba(0xa07d3aff).into()), ("text.literal".into(), rgba(0xa07d3aff).into())], + }, + status_bar: rgba(0x333944ff).into(), + title_bar: rgba(0x333944ff).into(), + toolbar: rgba(0x282c33ff).into(), + tab_bar: rgba(0x2b3038ff).into(), + editor: rgba(0x282c33ff).into(), + editor_subheader: rgba(0x2b3038ff).into(), + editor_active_line: rgba(0x2b3038ff).into(), + terminal: rgba(0x282c33ff).into(), + image_fallback_background: rgba(0x333944ff).into(), + git_created: rgba(0x83a598ff).into(), + git_modified: rgba(0x518b8bff).into(), + git_deleted: rgba(0xb3627aff).into(), + git_conflict: rgba(0xa07d3aff).into(), + git_ignored: rgba(0x827568ff).into(), + git_renamed: rgba(0xa07d3aff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x518b8bff).into(), + selection: rgba(0x518b8b3d).into(), }, - transparent: rgba(0x00000000).into(), - mac_os_traffic_light_red: rgba(0xec695eff).into(), - mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), - mac_os_traffic_light_green: rgba(0x61c553ff).into(), - border: rgba(0x3d4350ff).into(), - border_variant: rgba(0x3d4350ff).into(), - border_focused: rgba(0x223131ff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0x333944ff).into(), - surface: rgba(0x2b3038ff).into(), - background: rgba(0x333944ff).into(), - filled_element: rgba(0x333944ff).into(), - filled_element_hover: rgba(0xffffff1e).into(), - filled_element_active: rgba(0xffffff28).into(), - filled_element_selected: rgba(0x171e1eff).into(), - filled_element_disabled: rgba(0x00000000).into(), - ghost_element: rgba(0x00000000).into(), - ghost_element_hover: rgba(0xffffff14).into(), - ghost_element_active: rgba(0xffffff1e).into(), - ghost_element_selected: rgba(0x171e1eff).into(), - ghost_element_disabled: rgba(0x00000000).into(), - text: rgba(0xfdf4c1ff).into(), - text_muted: rgba(0xa69782ff).into(), - text_placeholder: rgba(0xb3627aff).into(), - text_disabled: rgba(0x827568ff).into(), - text_accent: rgba(0x518b8bff).into(), - icon_muted: rgba(0xa69782ff).into(), - syntax: SyntaxTheme { - highlights: vec![ - ("string.special.symbol".into(), rgba(0xa07d3aff).into()), - ("enum".into(), rgba(0xa07d3aff).into()), - ("punctuation.bracket".into(), rgba(0xd5c5a1ff).into()), - ("hint".into(), rgba(0x727d68ff).into()), - ("punctuation.delimiter".into(), rgba(0xd5c5a1ff).into()), - ("comment".into(), rgba(0xa89984ff).into()), - ("embedded".into(), rgba(0xfdf4c1ff).into()), - ("string".into(), rgba(0xa07d3aff).into()), - ("string.escape".into(), rgba(0xa89984ff).into()), - ("comment.doc".into(), rgba(0xa89984ff).into()), - ("variant".into(), rgba(0x518b8bff).into()), - ("predictive".into(), rgba(0x5c6152ff).into()), - ("link_text".into(), rgba(0xa07d3aff).into()), - ("attribute".into(), rgba(0x518b8bff).into()), - ("title".into(), rgba(0xfdf4c1ff).into()), - ("emphasis.strong".into(), rgba(0x518b8bff).into()), - ("primary".into(), rgba(0xfdf4c1ff).into()), - ("punctuation.list_marker".into(), rgba(0xd5c5a1ff).into()), - ("boolean".into(), rgba(0x83a598ff).into()), - ("function".into(), rgba(0xa07d3aff).into()), - ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), - ("string.special".into(), rgba(0xa07d3aff).into()), - ("string.regex".into(), rgba(0xa07d3aff).into()), - ("tag".into(), rgba(0x518b8bff).into()), - ("keyword".into(), rgba(0x518b8bff).into()), - ("type".into(), rgba(0x83a598ff).into()), - ("text.literal".into(), rgba(0xa07d3aff).into()), - ("link_uri".into(), rgba(0x83a598ff).into()), - ("label".into(), rgba(0x518b8bff).into()), - ("property".into(), rgba(0x518b8bff).into()), - ("number".into(), rgba(0x83a598ff).into()), - ("constructor".into(), rgba(0x518b8bff).into()), - ("preproc".into(), rgba(0xfdf4c1ff).into()), - ("emphasis".into(), rgba(0x518b8bff).into()), - ("variable".into(), rgba(0xfdf4c1ff).into()), - ("operator".into(), rgba(0xa07d3aff).into()), - ("punctuation".into(), rgba(0xd5c5a1ff).into()), - ("constant".into(), rgba(0x83a598ff).into()), - ], + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), }, - status_bar: rgba(0x333944ff).into(), - title_bar: rgba(0x333944ff).into(), - toolbar: rgba(0x282c33ff).into(), - tab_bar: rgba(0x2b3038ff).into(), - editor: rgba(0x282c33ff).into(), - editor_subheader: rgba(0x2b3038ff).into(), - editor_active_line: rgba(0x2b3038ff).into(), - terminal: rgba(0x282c33ff).into(), - image_fallback_background: rgba(0x333944ff).into(), - git_created: rgba(0x83a598ff).into(), - git_modified: rgba(0x518b8bff).into(), - git_deleted: rgba(0xb3627aff).into(), - git_conflict: rgba(0xa07d3aff).into(), - git_ignored: rgba(0x827568ff).into(), - git_renamed: rgba(0xa07d3aff).into(), - players: [ - PlayerTheme { - cursor: rgba(0x518b8bff).into(), - selection: rgba(0x518b8b3d).into(), - }, - PlayerTheme { - cursor: rgba(0x83a598ff).into(), - selection: rgba(0x83a5983d).into(), - }, - PlayerTheme { - cursor: rgba(0xa87222ff).into(), - selection: rgba(0xa872223d).into(), - }, - PlayerTheme { - cursor: rgba(0xa07d3aff).into(), - selection: rgba(0xa07d3a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xd75f5fff).into(), - selection: rgba(0xd75f5f3d).into(), - }, - PlayerTheme { - cursor: rgba(0x83a598ff).into(), - selection: rgba(0x83a5983d).into(), - }, - PlayerTheme { - cursor: rgba(0xb3627aff).into(), - selection: rgba(0xb3627a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xa07d3aff).into(), - selection: rgba(0xa07d3a3d).into(), - }, - ], - } -} + PlayerTheme { + cursor: rgba(0xa87222ff).into(), + selection: rgba(0xa872223d).into(), + }, + PlayerTheme { + cursor: rgba(0xa07d3aff).into(), + selection: rgba(0xa07d3a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd75f5fff).into(), + selection: rgba(0xd75f5f3d).into(), + }, + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), + }, + PlayerTheme { + cursor: rgba(0xb3627aff).into(), + selection: rgba(0xb3627a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xa07d3aff).into(), + selection: rgba(0xa07d3a3d).into(), + }, + ], +} \ No newline at end of file diff --git a/crates/theme_converter/Cargo.toml b/crates/theme_converter/Cargo.toml index 2a36dfce67..0ec692b7cc 100644 --- a/crates/theme_converter/Cargo.toml +++ b/crates/theme_converter/Cargo.toml @@ -9,6 +9,7 @@ publish = false [dependencies] anyhow.workspace = true clap = { version = "4.4", features = ["derive", "string"] } +convert_case = "0.6.0" gpui2 = { path = "../gpui2" } log.workspace = true rust-embed.workspace = true diff --git a/crates/theme_converter/src/main.rs b/crates/theme_converter/src/main.rs index ec31296c1d..3c2ab4ee90 100644 --- a/crates/theme_converter/src/main.rs +++ b/crates/theme_converter/src/main.rs @@ -3,9 +3,14 @@ mod theme_printer; use std::borrow::Cow; use std::collections::HashMap; use std::fmt::{self, Debug}; +use std::fs::{self, File}; +use std::io::Write; +use std::path::PathBuf; +use std::str::FromStr; use anyhow::{anyhow, Context, Result}; use clap::Parser; +use convert_case::{Case, Casing}; use gpui2::{hsla, rgb, serde_json, AssetSource, Hsla, SharedString}; use log::LevelFilter; use rust_embed::RustEmbed; @@ -26,13 +31,31 @@ struct Args { fn main() -> Result<()> { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - let args = Args::parse(); + // let args = Args::parse(); - let (json_theme, legacy_theme) = load_theme(args.theme)?; + let themes_path = PathBuf::from_str("crates/theme2/src/themes")?; - let theme = convert_theme(json_theme, legacy_theme)?; + for theme_path in Assets.list("themes/")? { + let (_, theme_name) = theme_path.split_once("themes/").unwrap(); - println!("{:#?}", ThemePrinter::new(theme)); + if theme_name == ".gitkeep" { + continue; + } + + let (json_theme, legacy_theme) = load_theme(&theme_path)?; + + let theme = convert_theme(json_theme, legacy_theme)?; + + let theme_slug = theme.metadata.name.as_ref().to_case(Case::Snake); + + let mut output_file = File::create(themes_path.join(format!("{theme_slug}.rs")))?; + + let theme_module = format!("{:#?}", ThemePrinter::new(theme)); + + output_file.write_all(theme_module.as_bytes())?; + + // println!("{:#?}", ThemePrinter::new(theme)); + } Ok(()) } @@ -188,9 +211,9 @@ struct JsonSyntaxStyle { } /// Loads the [`Theme`] with the given name. -fn load_theme(name: String) -> Result<(JsonTheme, LegacyTheme)> { - let theme_contents = Assets::get(&format!("themes/{name}.json")) - .with_context(|| format!("theme file not found: '{name}'"))?; +fn load_theme(theme_path: &str) -> Result<(JsonTheme, LegacyTheme)> { + let theme_contents = + Assets::get(theme_path).with_context(|| format!("theme file not found: '{theme_path}'"))?; let json_theme: JsonTheme = serde_json::from_str(std::str::from_utf8(&theme_contents.data)?) .context("failed to parse legacy theme")?; From 2879d231b114a2d918634f71e680d9ce0f7868b3 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 11:06:45 -0400 Subject: [PATCH 24/54] Revert changes to themes --- crates/theme2/src/themes/one_dark.rs | 212 +++++++++++++++---------- crates/theme2/src/themes/sandcastle.rs | 211 ++++++++++++++---------- 2 files changed, 257 insertions(+), 166 deletions(-) diff --git a/crates/theme2/src/themes/one_dark.rs b/crates/theme2/src/themes/one_dark.rs index d9be9043d6..c59f4da16a 100644 --- a/crates/theme2/src/themes/one_dark.rs +++ b/crates/theme2/src/themes/one_dark.rs @@ -1,85 +1,131 @@ -Theme { - metadata: ThemeMetadata { - name: "One Dark".into(), - is_light: false, - }, - transparent: rgba(0x00000000).into(), - mac_os_traffic_light_red: rgba(0xec695eff).into(), - mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), - mac_os_traffic_light_green: rgba(0x61c553ff).into(), - border: rgba(0x464b57ff).into(), - border_variant: rgba(0x464b57ff).into(), - border_focused: rgba(0x293b5bff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0x3b414dff).into(), - surface: rgba(0x2f343eff).into(), - background: rgba(0x3b414dff).into(), - filled_element: rgba(0x3b414dff).into(), - filled_element_hover: rgba(0xffffff1e).into(), - filled_element_active: rgba(0xffffff28).into(), - filled_element_selected: rgba(0x18243dff).into(), - filled_element_disabled: rgba(0x00000000).into(), - ghost_element: rgba(0x00000000).into(), - ghost_element_hover: rgba(0xffffff14).into(), - ghost_element_active: rgba(0xffffff1e).into(), - ghost_element_selected: rgba(0x18243dff).into(), - ghost_element_disabled: rgba(0x00000000).into(), - text: rgba(0xc8ccd4ff).into(), - text_muted: rgba(0x838994ff).into(), - text_placeholder: rgba(0xd07277ff).into(), - text_disabled: rgba(0x555a63ff).into(), - text_accent: rgba(0x74ade8ff).into(), - icon_muted: rgba(0x838994ff).into(), - syntax: SyntaxTheme { - highlights: vec![("punctuation".into(), rgba(0xacb2beff).into()), ("comment".into(), rgba(0x5d636fff).into()), ("predictive".into(), rgba(0x5a6a87ff).into()), ("comment.doc".into(), rgba(0x878e98ff).into()), ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), ("constant".into(), rgba(0xdfc184ff).into()), ("text.literal".into(), rgba(0xa1c181ff).into()), ("attribute".into(), rgba(0x74ade8ff).into()), ("link_text".into(), rgba(0x73ade9ff).into()), ("primary".into(), rgba(0xacb2beff).into()), ("enum".into(), rgba(0xd07277ff).into()), ("emphasis.strong".into(), rgba(0xbf956aff).into()), ("property".into(), rgba(0xd07277ff).into()), ("emphasis".into(), rgba(0x74ade8ff).into()), ("string.regex".into(), rgba(0xbf956aff).into()), ("variable".into(), rgba(0xc8ccd4ff).into()), ("keyword".into(), rgba(0xb477cfff).into()), ("boolean".into(), rgba(0xbf956aff).into()), ("variable.special".into(), rgba(0xbf956aff).into()), ("punctuation.special".into(), rgba(0xb1574bff).into()), ("title".into(), rgba(0xd07277ff).into()), ("punctuation.list_marker".into(), rgba(0xd07277ff).into()), ("string.special".into(), rgba(0xbf956aff).into()), ("number".into(), rgba(0xbf956aff).into()), ("preproc".into(), rgba(0xc8ccd4ff).into()), ("constructor".into(), rgba(0x73ade9ff).into()), ("string.special.symbol".into(), rgba(0xbf956aff).into()), ("link_uri".into(), rgba(0x6eb4bfff).into()), ("string".into(), rgba(0xa1c181ff).into()), ("string.escape".into(), rgba(0x878e98ff).into()), ("operator".into(), rgba(0x6eb4bfff).into()), ("hint".into(), rgba(0x5a6f89ff).into()), ("label".into(), rgba(0x74ade8ff).into()), ("type".into(), rgba(0x6eb4bfff).into()), ("embedded".into(), rgba(0xc8ccd4ff).into()), ("function".into(), rgba(0x73ade9ff).into()), ("tag".into(), rgba(0x74ade8ff).into()), ("variant".into(), rgba(0x73ade9ff).into()), ("punctuation.delimiter".into(), rgba(0xb2b9c6ff).into())], - }, - status_bar: rgba(0x3b414dff).into(), - title_bar: rgba(0x3b414dff).into(), - toolbar: rgba(0x282c33ff).into(), - tab_bar: rgba(0x2f343eff).into(), - editor: rgba(0x282c33ff).into(), - editor_subheader: rgba(0x2f343eff).into(), - editor_active_line: rgba(0x2f343eff).into(), - terminal: rgba(0x282c33ff).into(), - image_fallback_background: rgba(0x3b414dff).into(), - git_created: rgba(0xa1c181ff).into(), - git_modified: rgba(0x74ade8ff).into(), - git_deleted: rgba(0xd07277ff).into(), - git_conflict: rgba(0xdec184ff).into(), - git_ignored: rgba(0x555a63ff).into(), - git_renamed: rgba(0xdec184ff).into(), - players: [ - PlayerTheme { - cursor: rgba(0x74ade8ff).into(), - selection: rgba(0x74ade83d).into(), +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn one_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "One Dark".into(), + is_light: false, }, - PlayerTheme { - cursor: rgba(0xa1c181ff).into(), - selection: rgba(0xa1c1813d).into(), + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x464b57ff).into(), + border_variant: rgba(0x464b57ff).into(), + border_focused: rgba(0x293b5bff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x3b414dff).into(), + surface: rgba(0x2f343eff).into(), + background: rgba(0x3b414dff).into(), + filled_element: rgba(0x3b414dff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x18243dff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x18243dff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xc8ccd4ff).into(), + text_muted: rgba(0x838994ff).into(), + text_placeholder: rgba(0xd07277ff).into(), + text_disabled: rgba(0x555a63ff).into(), + text_accent: rgba(0x74ade8ff).into(), + icon_muted: rgba(0x838994ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("link_uri".into(), rgba(0x6eb4bfff).into()), + ("number".into(), rgba(0xbf956aff).into()), + ("property".into(), rgba(0xd07277ff).into()), + ("boolean".into(), rgba(0xbf956aff).into()), + ("label".into(), rgba(0x74ade8ff).into()), + ("punctuation.list_marker".into(), rgba(0xd07277ff).into()), + ("keyword".into(), rgba(0xb477cfff).into()), + ("punctuation.delimiter".into(), rgba(0xb2b9c6ff).into()), + ("string.special".into(), rgba(0xbf956aff).into()), + ("constant".into(), rgba(0xdfc184ff).into()), + ("punctuation".into(), rgba(0xacb2beff).into()), + ("variable.special".into(), rgba(0xbf956aff).into()), + ("preproc".into(), rgba(0xc8ccd4ff).into()), + ("enum".into(), rgba(0xd07277ff).into()), + ("attribute".into(), rgba(0x74ade8ff).into()), + ("emphasis.strong".into(), rgba(0xbf956aff).into()), + ("title".into(), rgba(0xd07277ff).into()), + ("hint".into(), rgba(0x5a6f89ff).into()), + ("emphasis".into(), rgba(0x74ade8ff).into()), + ("string.regex".into(), rgba(0xbf956aff).into()), + ("link_text".into(), rgba(0x73ade9ff).into()), + ("string".into(), rgba(0xa1c181ff).into()), + ("comment.doc".into(), rgba(0x878e98ff).into()), + ("punctuation.special".into(), rgba(0xb1574bff).into()), + ("primary".into(), rgba(0xacb2beff).into()), + ("operator".into(), rgba(0x6eb4bfff).into()), + ("function".into(), rgba(0x73ade9ff).into()), + ("string.special.symbol".into(), rgba(0xbf956aff).into()), + ("type".into(), rgba(0x6eb4bfff).into()), + ("variant".into(), rgba(0x73ade9ff).into()), + ("tag".into(), rgba(0x74ade8ff).into()), + ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), + ("embedded".into(), rgba(0xc8ccd4ff).into()), + ("string.escape".into(), rgba(0x878e98ff).into()), + ("variable".into(), rgba(0xc8ccd4ff).into()), + ("predictive".into(), rgba(0x5a6a87ff).into()), + ("comment".into(), rgba(0x5d636fff).into()), + ("text.literal".into(), rgba(0xa1c181ff).into()), + ("constructor".into(), rgba(0x73ade9ff).into()), + ], }, - PlayerTheme { - cursor: rgba(0xbe5046ff).into(), - selection: rgba(0xbe50463d).into(), - }, - PlayerTheme { - cursor: rgba(0xbf956aff).into(), - selection: rgba(0xbf956a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xb477cfff).into(), - selection: rgba(0xb477cf3d).into(), - }, - PlayerTheme { - cursor: rgba(0x6eb4bfff).into(), - selection: rgba(0x6eb4bf3d).into(), - }, - PlayerTheme { - cursor: rgba(0xd07277ff).into(), - selection: rgba(0xd072773d).into(), - }, - PlayerTheme { - cursor: rgba(0xdec184ff).into(), - selection: rgba(0xdec1843d).into(), - }, - ], -} \ No newline at end of file + status_bar: rgba(0x3b414dff).into(), + title_bar: rgba(0x3b414dff).into(), + toolbar: rgba(0x282c33ff).into(), + tab_bar: rgba(0x2f343eff).into(), + editor: rgba(0x282c33ff).into(), + editor_subheader: rgba(0x2f343eff).into(), + editor_active_line: rgba(0x2f343eff).into(), + terminal: rgba(0x282c33ff).into(), + image_fallback_background: rgba(0x3b414dff).into(), + git_created: rgba(0xa1c181ff).into(), + git_modified: rgba(0x74ade8ff).into(), + git_deleted: rgba(0xd07277ff).into(), + git_conflict: rgba(0xdec184ff).into(), + git_ignored: rgba(0x555a63ff).into(), + git_renamed: rgba(0xdec184ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x74ade8ff).into(), + selection: rgba(0x74ade83d).into(), + }, + PlayerTheme { + cursor: rgba(0xa1c181ff).into(), + selection: rgba(0xa1c1813d).into(), + }, + PlayerTheme { + cursor: rgba(0xbe5046ff).into(), + selection: rgba(0xbe50463d).into(), + }, + PlayerTheme { + cursor: rgba(0xbf956aff).into(), + selection: rgba(0xbf956a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xb477cfff).into(), + selection: rgba(0xb477cf3d).into(), + }, + PlayerTheme { + cursor: rgba(0x6eb4bfff).into(), + selection: rgba(0x6eb4bf3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd07277ff).into(), + selection: rgba(0xd072773d).into(), + }, + PlayerTheme { + cursor: rgba(0xdec184ff).into(), + selection: rgba(0xdec1843d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/sandcastle.rs b/crates/theme2/src/themes/sandcastle.rs index 507c99209f..4e87c427f0 100644 --- a/crates/theme2/src/themes/sandcastle.rs +++ b/crates/theme2/src/themes/sandcastle.rs @@ -1,85 +1,130 @@ -Theme { - metadata: ThemeMetadata { - name: "Sandcastle".into(), - is_light: false, - }, - transparent: rgba(0x00000000).into(), - mac_os_traffic_light_red: rgba(0xec695eff).into(), - mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), - mac_os_traffic_light_green: rgba(0x61c553ff).into(), - border: rgba(0x3d4350ff).into(), - border_variant: rgba(0x3d4350ff).into(), - border_focused: rgba(0x223131ff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0x333944ff).into(), - surface: rgba(0x2b3038ff).into(), - background: rgba(0x333944ff).into(), - filled_element: rgba(0x333944ff).into(), - filled_element_hover: rgba(0xffffff1e).into(), - filled_element_active: rgba(0xffffff28).into(), - filled_element_selected: rgba(0x171e1eff).into(), - filled_element_disabled: rgba(0x00000000).into(), - ghost_element: rgba(0x00000000).into(), - ghost_element_hover: rgba(0xffffff14).into(), - ghost_element_active: rgba(0xffffff1e).into(), - ghost_element_selected: rgba(0x171e1eff).into(), - ghost_element_disabled: rgba(0x00000000).into(), - text: rgba(0xfdf4c1ff).into(), - text_muted: rgba(0xa69782ff).into(), - text_placeholder: rgba(0xb3627aff).into(), - text_disabled: rgba(0x827568ff).into(), - text_accent: rgba(0x518b8bff).into(), - icon_muted: rgba(0xa69782ff).into(), - syntax: SyntaxTheme { - highlights: vec![("variable".into(), rgba(0xfdf4c1ff).into()), ("comment.doc".into(), rgba(0xa89984ff).into()), ("punctuation".into(), rgba(0xd5c5a1ff).into()), ("comment".into(), rgba(0xa89984ff).into()), ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), ("string.regex".into(), rgba(0xa07d3aff).into()), ("emphasis".into(), rgba(0x518b8bff).into()), ("link_uri".into(), rgba(0x83a598ff).into()), ("label".into(), rgba(0x518b8bff).into()), ("property".into(), rgba(0x518b8bff).into()), ("constant".into(), rgba(0x83a598ff).into()), ("enum".into(), rgba(0xa07d3aff).into()), ("number".into(), rgba(0x83a598ff).into()), ("link_text".into(), rgba(0xa07d3aff).into()), ("hint".into(), rgba(0x727d68ff).into()), ("type".into(), rgba(0x83a598ff).into()), ("title".into(), rgba(0xfdf4c1ff).into()), ("punctuation.delimiter".into(), rgba(0xd5c5a1ff).into()), ("function".into(), rgba(0xa07d3aff).into()), ("preproc".into(), rgba(0xfdf4c1ff).into()), ("boolean".into(), rgba(0x83a598ff).into()), ("keyword".into(), rgba(0x518b8bff).into()), ("attribute".into(), rgba(0x518b8bff).into()), ("predictive".into(), rgba(0x5c6152ff).into()), ("string".into(), rgba(0xa07d3aff).into()), ("emphasis.strong".into(), rgba(0x518b8bff).into()), ("punctuation.bracket".into(), rgba(0xd5c5a1ff).into()), ("string.special.symbol".into(), rgba(0xa07d3aff).into()), ("variant".into(), rgba(0x518b8bff).into()), ("tag".into(), rgba(0x518b8bff).into()), ("primary".into(), rgba(0xfdf4c1ff).into()), ("string.special".into(), rgba(0xa07d3aff).into()), ("punctuation.list_marker".into(), rgba(0xd5c5a1ff).into()), ("string.escape".into(), rgba(0xa89984ff).into()), ("constructor".into(), rgba(0x518b8bff).into()), ("embedded".into(), rgba(0xfdf4c1ff).into()), ("operator".into(), rgba(0xa07d3aff).into()), ("text.literal".into(), rgba(0xa07d3aff).into())], - }, - status_bar: rgba(0x333944ff).into(), - title_bar: rgba(0x333944ff).into(), - toolbar: rgba(0x282c33ff).into(), - tab_bar: rgba(0x2b3038ff).into(), - editor: rgba(0x282c33ff).into(), - editor_subheader: rgba(0x2b3038ff).into(), - editor_active_line: rgba(0x2b3038ff).into(), - terminal: rgba(0x282c33ff).into(), - image_fallback_background: rgba(0x333944ff).into(), - git_created: rgba(0x83a598ff).into(), - git_modified: rgba(0x518b8bff).into(), - git_deleted: rgba(0xb3627aff).into(), - git_conflict: rgba(0xa07d3aff).into(), - git_ignored: rgba(0x827568ff).into(), - git_renamed: rgba(0xa07d3aff).into(), - players: [ - PlayerTheme { - cursor: rgba(0x518b8bff).into(), - selection: rgba(0x518b8b3d).into(), +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn sandcastle() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Sandcastle".into(), + is_light: false, }, - PlayerTheme { - cursor: rgba(0x83a598ff).into(), - selection: rgba(0x83a5983d).into(), + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x3d4350ff).into(), + border_variant: rgba(0x3d4350ff).into(), + border_focused: rgba(0x223131ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x333944ff).into(), + surface: rgba(0x2b3038ff).into(), + background: rgba(0x333944ff).into(), + filled_element: rgba(0x333944ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x171e1eff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x171e1eff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfdf4c1ff).into(), + text_muted: rgba(0xa69782ff).into(), + text_placeholder: rgba(0xb3627aff).into(), + text_disabled: rgba(0x827568ff).into(), + text_accent: rgba(0x518b8bff).into(), + icon_muted: rgba(0xa69782ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.special.symbol".into(), rgba(0xa07d3aff).into()), + ("enum".into(), rgba(0xa07d3aff).into()), + ("punctuation.bracket".into(), rgba(0xd5c5a1ff).into()), + ("hint".into(), rgba(0x727d68ff).into()), + ("punctuation.delimiter".into(), rgba(0xd5c5a1ff).into()), + ("comment".into(), rgba(0xa89984ff).into()), + ("embedded".into(), rgba(0xfdf4c1ff).into()), + ("string".into(), rgba(0xa07d3aff).into()), + ("string.escape".into(), rgba(0xa89984ff).into()), + ("comment.doc".into(), rgba(0xa89984ff).into()), + ("variant".into(), rgba(0x518b8bff).into()), + ("predictive".into(), rgba(0x5c6152ff).into()), + ("link_text".into(), rgba(0xa07d3aff).into()), + ("attribute".into(), rgba(0x518b8bff).into()), + ("title".into(), rgba(0xfdf4c1ff).into()), + ("emphasis.strong".into(), rgba(0x518b8bff).into()), + ("primary".into(), rgba(0xfdf4c1ff).into()), + ("punctuation.list_marker".into(), rgba(0xd5c5a1ff).into()), + ("boolean".into(), rgba(0x83a598ff).into()), + ("function".into(), rgba(0xa07d3aff).into()), + ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), + ("string.special".into(), rgba(0xa07d3aff).into()), + ("string.regex".into(), rgba(0xa07d3aff).into()), + ("tag".into(), rgba(0x518b8bff).into()), + ("keyword".into(), rgba(0x518b8bff).into()), + ("type".into(), rgba(0x83a598ff).into()), + ("text.literal".into(), rgba(0xa07d3aff).into()), + ("link_uri".into(), rgba(0x83a598ff).into()), + ("label".into(), rgba(0x518b8bff).into()), + ("property".into(), rgba(0x518b8bff).into()), + ("number".into(), rgba(0x83a598ff).into()), + ("constructor".into(), rgba(0x518b8bff).into()), + ("preproc".into(), rgba(0xfdf4c1ff).into()), + ("emphasis".into(), rgba(0x518b8bff).into()), + ("variable".into(), rgba(0xfdf4c1ff).into()), + ("operator".into(), rgba(0xa07d3aff).into()), + ("punctuation".into(), rgba(0xd5c5a1ff).into()), + ("constant".into(), rgba(0x83a598ff).into()), + ], }, - PlayerTheme { - cursor: rgba(0xa87222ff).into(), - selection: rgba(0xa872223d).into(), - }, - PlayerTheme { - cursor: rgba(0xa07d3aff).into(), - selection: rgba(0xa07d3a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xd75f5fff).into(), - selection: rgba(0xd75f5f3d).into(), - }, - PlayerTheme { - cursor: rgba(0x83a598ff).into(), - selection: rgba(0x83a5983d).into(), - }, - PlayerTheme { - cursor: rgba(0xb3627aff).into(), - selection: rgba(0xb3627a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xa07d3aff).into(), - selection: rgba(0xa07d3a3d).into(), - }, - ], -} \ No newline at end of file + status_bar: rgba(0x333944ff).into(), + title_bar: rgba(0x333944ff).into(), + toolbar: rgba(0x282c33ff).into(), + tab_bar: rgba(0x2b3038ff).into(), + editor: rgba(0x282c33ff).into(), + editor_subheader: rgba(0x2b3038ff).into(), + editor_active_line: rgba(0x2b3038ff).into(), + terminal: rgba(0x282c33ff).into(), + image_fallback_background: rgba(0x333944ff).into(), + git_created: rgba(0x83a598ff).into(), + git_modified: rgba(0x518b8bff).into(), + git_deleted: rgba(0xb3627aff).into(), + git_conflict: rgba(0xa07d3aff).into(), + git_ignored: rgba(0x827568ff).into(), + git_renamed: rgba(0xa07d3aff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x518b8bff).into(), + selection: rgba(0x518b8b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), + }, + PlayerTheme { + cursor: rgba(0xa87222ff).into(), + selection: rgba(0xa872223d).into(), + }, + PlayerTheme { + cursor: rgba(0xa07d3aff).into(), + selection: rgba(0xa07d3a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd75f5fff).into(), + selection: rgba(0xd75f5f3d).into(), + }, + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), + }, + PlayerTheme { + cursor: rgba(0xb3627aff).into(), + selection: rgba(0xb3627a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xa07d3aff).into(), + selection: rgba(0xa07d3a3d).into(), + }, + ], + } +} From f3c113fe02489748823b67f6e3340a094d412795 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 11:07:24 -0400 Subject: [PATCH 25/54] clean up warnings and fix tests in the ai crate --- crates/ai/src/completion.rs | 7 + crates/ai/src/prompts/base.rs | 4 +- crates/ai/src/providers/open_ai/completion.rs | 8 + crates/ai/src/test.rs | 14 ++ crates/assistant/src/assistant_panel.rs | 212 ++++++------------ 5 files changed, 102 insertions(+), 143 deletions(-) diff --git a/crates/ai/src/completion.rs b/crates/ai/src/completion.rs index 7fdc49e918..30a60fcf1d 100644 --- a/crates/ai/src/completion.rs +++ b/crates/ai/src/completion.rs @@ -13,4 +13,11 @@ pub trait CompletionProvider: CredentialProvider { &self, prompt: Box, ) -> BoxFuture<'static, Result>>>; + fn box_clone(&self) -> Box; +} + +impl Clone for Box { + fn clone(&self) -> Box { + self.box_clone() + } } diff --git a/crates/ai/src/prompts/base.rs b/crates/ai/src/prompts/base.rs index a2106c7410..75bad00154 100644 --- a/crates/ai/src/prompts/base.rs +++ b/crates/ai/src/prompts/base.rs @@ -147,7 +147,7 @@ pub(crate) mod tests { content = args.model.truncate( &content, max_token_length, - TruncationDirection::Start, + TruncationDirection::End, )?; token_count = max_token_length; } @@ -172,7 +172,7 @@ pub(crate) mod tests { content = args.model.truncate( &content, max_token_length, - TruncationDirection::Start, + TruncationDirection::End, )?; token_count = max_token_length; } diff --git a/crates/ai/src/providers/open_ai/completion.rs b/crates/ai/src/providers/open_ai/completion.rs index 02d25a7eec..94685fd233 100644 --- a/crates/ai/src/providers/open_ai/completion.rs +++ b/crates/ai/src/providers/open_ai/completion.rs @@ -193,6 +193,7 @@ pub async fn stream_completion( } } +#[derive(Clone)] pub struct OpenAICompletionProvider { model: OpenAILanguageModel, credential: Arc>, @@ -271,6 +272,10 @@ impl CompletionProvider for OpenAICompletionProvider { &self, prompt: Box, ) -> BoxFuture<'static, Result>>> { + // Currently the CompletionRequest for OpenAI, includes a 'model' parameter + // This means that the model is determined by the CompletionRequest and not the CompletionProvider, + // which is currently model based, due to the langauge model. + // At some point in the future we should rectify this. let credential = self.credential.read().clone(); let request = stream_completion(credential, self.executor.clone(), prompt); async move { @@ -287,4 +292,7 @@ impl CompletionProvider for OpenAICompletionProvider { } .boxed() } + fn box_clone(&self) -> Box { + Box::new((*self).clone()) + } } diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index bc9a6a3e43..d4165f3cca 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -33,7 +33,10 @@ impl LanguageModel for FakeLanguageModel { length: usize, direction: TruncationDirection, ) -> anyhow::Result { + println!("TRYING TO TRUNCATE: {:?}", length.clone()); + if length > self.count_tokens(content)? { + println!("NOT TRUNCATING"); return anyhow::Ok(content.to_string()); } @@ -133,6 +136,14 @@ pub struct FakeCompletionProvider { last_completion_tx: Mutex>>, } +impl Clone for FakeCompletionProvider { + fn clone(&self) -> Self { + Self { + last_completion_tx: Mutex::new(None), + } + } +} + impl FakeCompletionProvider { pub fn new() -> Self { Self { @@ -174,4 +185,7 @@ impl CompletionProvider for FakeCompletionProvider { *self.last_completion_tx.lock() = Some(tx); async move { Ok(rx.map(|rx| Ok(rx)).boxed()) }.boxed() } + fn box_clone(&self) -> Box { + Box::new((*self).clone()) + } } diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index c10ad2c362..d0c7e7e883 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -9,9 +9,7 @@ use crate::{ use ai::{ auth::ProviderCredential, completion::{CompletionProvider, CompletionRequest}, - providers::open_ai::{ - stream_completion, OpenAICompletionProvider, OpenAIRequest, RequestMessage, - }, + providers::open_ai::{OpenAICompletionProvider, OpenAIRequest, RequestMessage}, }; use ai::prompts::repository_context::PromptCodeSnippet; @@ -47,7 +45,7 @@ use search::BufferSearchBar; use semantic_index::{SemanticIndex, SemanticIndexStatus}; use settings::SettingsStore; use std::{ - cell::{Cell, RefCell}, + cell::Cell, cmp, fmt::Write, iter, @@ -144,10 +142,8 @@ pub struct AssistantPanel { zoomed: bool, has_focus: bool, toolbar: ViewHandle, - credential: Rc>, completion_provider: Box, api_key_editor: Option>, - has_read_credentials: bool, languages: Arc, fs: Arc, subscriptions: Vec, @@ -223,10 +219,8 @@ impl AssistantPanel { zoomed: false, has_focus: false, toolbar, - credential: Rc::new(RefCell::new(ProviderCredential::NoCredentials)), completion_provider, api_key_editor: None, - has_read_credentials: false, languages: workspace.app_state().languages.clone(), fs: workspace.app_state().fs.clone(), width: None, @@ -265,7 +259,7 @@ impl AssistantPanel { cx: &mut ViewContext, ) { let this = if let Some(this) = workspace.panel::(cx) { - if this.update(cx, |assistant, cx| assistant.has_credentials(cx)) { + if this.update(cx, |assistant, _| assistant.has_credentials()) { this } else { workspace.focus_panel::(cx); @@ -331,6 +325,9 @@ impl AssistantPanel { cx.background().clone(), )); + // Retrieve Credentials Authenticates the Provider + // provider.retrieve_credentials(cx); + let codegen = cx.add_model(|cx| { Codegen::new(editor.read(cx).buffer().clone(), codegen_kind, provider, cx) }); @@ -814,7 +811,7 @@ impl AssistantPanel { fn new_conversation(&mut self, cx: &mut ViewContext) -> ViewHandle { let editor = cx.add_view(|cx| { ConversationEditor::new( - self.credential.clone(), + self.completion_provider.clone(), self.languages.clone(), self.fs.clone(), self.workspace.clone(), @@ -883,9 +880,8 @@ impl AssistantPanel { let credential = ProviderCredential::Credentials { api_key: api_key.clone(), }; - self.completion_provider - .save_credentials(cx, credential.clone()); - *self.credential.borrow_mut() = credential; + + self.completion_provider.save_credentials(cx, credential); self.api_key_editor.take(); cx.focus_self(); @@ -898,7 +894,6 @@ impl AssistantPanel { fn reset_credentials(&mut self, _: &ResetKey, cx: &mut ViewContext) { self.completion_provider.delete_credentials(cx); - *self.credential.borrow_mut() = ProviderCredential::NoCredentials; self.api_key_editor = Some(build_api_key_editor(cx)); cx.focus_self(); cx.notify(); @@ -1157,19 +1152,12 @@ impl AssistantPanel { let fs = self.fs.clone(); let workspace = self.workspace.clone(); - let credential = self.credential.clone(); let languages = self.languages.clone(); cx.spawn(|this, mut cx| async move { let saved_conversation = fs.load(&path).await?; let saved_conversation = serde_json::from_str(&saved_conversation)?; let conversation = cx.add_model(|cx| { - Conversation::deserialize( - saved_conversation, - path.clone(), - credential, - languages, - cx, - ) + Conversation::deserialize(saved_conversation, path.clone(), languages, cx) }); this.update(&mut cx, |this, cx| { // If, by the time we've loaded the conversation, the user has already opened @@ -1193,39 +1181,12 @@ impl AssistantPanel { .position(|editor| editor.read(cx).conversation.read(cx).path.as_deref() == Some(path)) } - fn has_credentials(&mut self, cx: &mut ViewContext) -> bool { - let credential = self.load_credentials(cx); - match credential { - ProviderCredential::Credentials { .. } => true, - ProviderCredential::NotNeeded => true, - ProviderCredential::NoCredentials => false, - } + fn has_credentials(&mut self) -> bool { + self.completion_provider.has_credentials() } - fn load_credentials(&mut self, cx: &mut ViewContext) -> ProviderCredential { - let existing_credential = self.credential.clone(); - let existing_credential = existing_credential.borrow().clone(); - match existing_credential { - ProviderCredential::NoCredentials => { - if !self.has_read_credentials { - self.has_read_credentials = true; - let retrieved_credentials = self.completion_provider.retrieve_credentials(cx); - - match retrieved_credentials { - ProviderCredential::NoCredentials {} => { - self.api_key_editor = Some(build_api_key_editor(cx)); - cx.notify(); - } - _ => { - *self.credential.borrow_mut() = retrieved_credentials; - } - } - } - } - _ => {} - } - - self.credential.borrow().clone() + fn load_credentials(&mut self, cx: &mut ViewContext) { + self.completion_provider.retrieve_credentials(cx); } } @@ -1475,10 +1436,10 @@ struct Conversation { token_count: Option, max_token_count: usize, pending_token_count: Task>, - credential: Rc>, pending_save: Task>, path: Option, _subscriptions: Vec, + completion_provider: Box, } impl Entity for Conversation { @@ -1487,10 +1448,9 @@ impl Entity for Conversation { impl Conversation { fn new( - credential: Rc>, - language_registry: Arc, cx: &mut ModelContext, + completion_provider: Box, ) -> Self { let markdown = language_registry.language_for_name("Markdown"); let buffer = cx.add_model(|cx| { @@ -1529,8 +1489,8 @@ impl Conversation { _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], pending_save: Task::ready(Ok(())), path: None, - credential, buffer, + completion_provider, }; let message = MessageAnchor { id: MessageId(post_inc(&mut this.next_message_id.0)), @@ -1576,7 +1536,6 @@ impl Conversation { fn deserialize( saved_conversation: SavedConversation, path: PathBuf, - credential: Rc>, language_registry: Arc, cx: &mut ModelContext, ) -> Self { @@ -1585,6 +1544,10 @@ impl Conversation { None => Some(Uuid::new_v4().to_string()), }; let model = saved_conversation.model; + let completion_provider: Box = Box::new( + OpenAICompletionProvider::new(model.full_name(), cx.background().clone()), + ); + completion_provider.retrieve_credentials(cx); let markdown = language_registry.language_for_name("Markdown"); let mut message_anchors = Vec::new(); let mut next_message_id = MessageId(0); @@ -1631,8 +1594,8 @@ impl Conversation { _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], pending_save: Task::ready(Ok(())), path: Some(path), - credential, buffer, + completion_provider, }; this.count_remaining_tokens(cx); this @@ -1753,12 +1716,8 @@ impl Conversation { } if should_assist { - let credential = self.credential.borrow().clone(); - match credential { - ProviderCredential::NoCredentials => { - return Default::default(); - } - _ => {} + if !self.completion_provider.has_credentials() { + return Default::default(); } let request: Box = Box::new(OpenAIRequest { @@ -1773,7 +1732,7 @@ impl Conversation { temperature: 1.0, }); - let stream = stream_completion(credential, cx.background().clone(), request); + let stream = self.completion_provider.complete(request); let assistant_message = self .insert_message_after(last_message_id, Role::Assistant, MessageStatus::Pending, cx) .unwrap(); @@ -1791,33 +1750,28 @@ impl Conversation { let mut messages = stream.await?; while let Some(message) = messages.next().await { - let mut message = message?; - if let Some(choice) = message.choices.pop() { - this.upgrade(&cx) - .ok_or_else(|| anyhow!("conversation was dropped"))? - .update(&mut cx, |this, cx| { - let text: Arc = choice.delta.content?.into(); - let message_ix = - this.message_anchors.iter().position(|message| { - message.id == assistant_message_id - })?; - this.buffer.update(cx, |buffer, cx| { - let offset = this.message_anchors[message_ix + 1..] - .iter() - .find(|message| message.start.is_valid(buffer)) - .map_or(buffer.len(), |message| { - message - .start - .to_offset(buffer) - .saturating_sub(1) - }); - buffer.edit([(offset..offset, text)], None, cx); - }); - cx.emit(ConversationEvent::StreamedCompletion); + let text = message?; - Some(()) + this.upgrade(&cx) + .ok_or_else(|| anyhow!("conversation was dropped"))? + .update(&mut cx, |this, cx| { + let message_ix = this + .message_anchors + .iter() + .position(|message| message.id == assistant_message_id)?; + this.buffer.update(cx, |buffer, cx| { + let offset = this.message_anchors[message_ix + 1..] + .iter() + .find(|message| message.start.is_valid(buffer)) + .map_or(buffer.len(), |message| { + message.start.to_offset(buffer).saturating_sub(1) + }); + buffer.edit([(offset..offset, text)], None, cx); }); - } + cx.emit(ConversationEvent::StreamedCompletion); + + Some(()) + }); smol::future::yield_now().await; } @@ -2039,13 +1993,8 @@ impl Conversation { fn summarize(&mut self, cx: &mut ModelContext) { if self.message_anchors.len() >= 2 && self.summary.is_none() { - let credential = self.credential.borrow().clone(); - - match credential { - ProviderCredential::NoCredentials => { - return; - } - _ => {} + if !self.completion_provider.has_credentials() { + return; } let messages = self @@ -2065,23 +2014,20 @@ impl Conversation { temperature: 1.0, }); - let stream = stream_completion(credential, cx.background().clone(), request); + let stream = self.completion_provider.complete(request); self.pending_summary = cx.spawn(|this, mut cx| { async move { let mut messages = stream.await?; while let Some(message) = messages.next().await { - let mut message = message?; - if let Some(choice) = message.choices.pop() { - let text = choice.delta.content.unwrap_or_default(); - this.update(&mut cx, |this, cx| { - this.summary - .get_or_insert(Default::default()) - .text - .push_str(&text); - cx.emit(ConversationEvent::SummaryChanged); - }); - } + let text = message?; + this.update(&mut cx, |this, cx| { + this.summary + .get_or_insert(Default::default()) + .text + .push_str(&text); + cx.emit(ConversationEvent::SummaryChanged); + }); } this.update(&mut cx, |this, cx| { @@ -2255,13 +2201,14 @@ struct ConversationEditor { impl ConversationEditor { fn new( - credential: Rc>, + completion_provider: Box, language_registry: Arc, fs: Arc, workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { - let conversation = cx.add_model(|cx| Conversation::new(credential, language_registry, cx)); + let conversation = + cx.add_model(|cx| Conversation::new(language_registry, cx, completion_provider)); Self::for_conversation(conversation, fs, workspace, cx) } @@ -3450,6 +3397,7 @@ fn merge_ranges(ranges: &mut Vec>, buffer: &MultiBufferSnapshot) { mod tests { use super::*; use crate::MessageId; + use ai::test::FakeCompletionProvider; use gpui::AppContext; #[gpui::test] @@ -3457,13 +3405,9 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| { - Conversation::new( - Rc::new(RefCell::new(ProviderCredential::NotNeeded)), - registry, - cx, - ) - }); + + let completion_provider = Box::new(FakeCompletionProvider::new()); + let conversation = cx.add_model(|cx| Conversation::new(registry, cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); let message_1 = conversation.read(cx).message_anchors[0].clone(); @@ -3591,13 +3535,9 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| { - Conversation::new( - Rc::new(RefCell::new(ProviderCredential::NotNeeded)), - registry, - cx, - ) - }); + let completion_provider = Box::new(FakeCompletionProvider::new()); + + let conversation = cx.add_model(|cx| Conversation::new(registry, cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); let message_1 = conversation.read(cx).message_anchors[0].clone(); @@ -3693,13 +3633,8 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| { - Conversation::new( - Rc::new(RefCell::new(ProviderCredential::NotNeeded)), - registry, - cx, - ) - }); + let completion_provider = Box::new(FakeCompletionProvider::new()); + let conversation = cx.add_model(|cx| Conversation::new(registry, cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); let message_1 = conversation.read(cx).message_anchors[0].clone(); @@ -3781,13 +3716,9 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let conversation = cx.add_model(|cx| { - Conversation::new( - Rc::new(RefCell::new(ProviderCredential::NotNeeded)), - registry.clone(), - cx, - ) - }); + let completion_provider = Box::new(FakeCompletionProvider::new()); + let conversation = + cx.add_model(|cx| Conversation::new(registry.clone(), cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); let message_0 = conversation.read(cx).message_anchors[0].id; let message_1 = conversation.update(cx, |conversation, cx| { @@ -3824,7 +3755,6 @@ mod tests { Conversation::deserialize( conversation.read(cx).serialize(cx), Default::default(), - Rc::new(RefCell::new(ProviderCredential::NotNeeded)), registry.clone(), cx, ) From 7a66ebae712ab27bc13a6a246393f30277ded59f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 11:21:33 -0400 Subject: [PATCH 26/54] Emit modules for each theme --- crates/theme_converter/src/main.rs | 48 +++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/crates/theme_converter/src/main.rs b/crates/theme_converter/src/main.rs index 3c2ab4ee90..cc0cdf9c99 100644 --- a/crates/theme_converter/src/main.rs +++ b/crates/theme_converter/src/main.rs @@ -3,7 +3,7 @@ mod theme_printer; use std::borrow::Cow; use std::collections::HashMap; use std::fmt::{self, Debug}; -use std::fs::{self, File}; +use std::fs::File; use std::io::Write; use std::path::PathBuf; use std::str::FromStr; @@ -35,6 +35,8 @@ fn main() -> Result<()> { let themes_path = PathBuf::from_str("crates/theme2/src/themes")?; + let mut theme_modules = Vec::new(); + for theme_path in Assets.list("themes/")? { let (_, theme_name) = theme_path.split_once("themes/").unwrap(); @@ -46,17 +48,55 @@ fn main() -> Result<()> { let theme = convert_theme(json_theme, legacy_theme)?; - let theme_slug = theme.metadata.name.as_ref().to_case(Case::Snake); + let theme_slug = theme + .metadata + .name + .as_ref() + .replace("é", "e") + .to_case(Case::Snake); let mut output_file = File::create(themes_path.join(format!("{theme_slug}.rs")))?; - let theme_module = format!("{:#?}", ThemePrinter::new(theme)); + let theme_module = format!( + r#" + use gpui2::rgba; + + use crate::{{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}}; + + pub fn {theme_slug}() -> Theme {{ + {theme_definition} + }} + "#, + theme_definition = format!("{:#?}", ThemePrinter::new(theme)) + ); output_file.write_all(theme_module.as_bytes())?; - // println!("{:#?}", ThemePrinter::new(theme)); + theme_modules.push(theme_slug); } + let mut mod_rs_file = File::create(themes_path.join(format!("mod.rs")))?; + + let mod_rs_contents = format!( + r#" + {mod_statements} + + {use_statements} + "#, + mod_statements = theme_modules + .iter() + .map(|module| format!("mod {module};")) + .collect::>() + .join("\n"), + use_statements = theme_modules + .iter() + .map(|module| format!("pub use {module}::*;")) + .collect::>() + .join("\n") + ); + + mod_rs_file.write_all(mod_rs_contents.as_bytes())?; + Ok(()) } From 3d8516b25ffad34e72ca58dd8bd967d8f3a1826e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 11:22:04 -0400 Subject: [PATCH 27/54] Convert all themes --- crates/theme2/src/registry.rs | 49 ++- crates/theme2/src/themes/andromeda.rs | 131 +++++++ crates/theme2/src/themes/atelier_cave_dark.rs | 137 ++++++++ .../theme2/src/themes/atelier_cave_light.rs | 137 ++++++++ crates/theme2/src/themes/atelier_dune_dark.rs | 137 ++++++++ .../theme2/src/themes/atelier_dune_light.rs | 137 ++++++++ .../theme2/src/themes/atelier_estuary_dark.rs | 137 ++++++++ .../src/themes/atelier_estuary_light.rs | 137 ++++++++ .../theme2/src/themes/atelier_forest_dark.rs | 137 ++++++++ .../theme2/src/themes/atelier_forest_light.rs | 137 ++++++++ .../theme2/src/themes/atelier_heath_dark.rs | 137 ++++++++ .../theme2/src/themes/atelier_heath_light.rs | 137 ++++++++ .../src/themes/atelier_lakeside_dark.rs | 137 ++++++++ .../src/themes/atelier_lakeside_light.rs | 137 ++++++++ .../theme2/src/themes/atelier_plateau_dark.rs | 137 ++++++++ .../src/themes/atelier_plateau_light.rs | 137 ++++++++ .../theme2/src/themes/atelier_savanna_dark.rs | 137 ++++++++ .../src/themes/atelier_savanna_light.rs | 137 ++++++++ .../theme2/src/themes/atelier_seaside_dark.rs | 137 ++++++++ .../src/themes/atelier_seaside_light.rs | 137 ++++++++ .../src/themes/atelier_sulphurpool_dark.rs | 137 ++++++++ .../src/themes/atelier_sulphurpool_light.rs | 137 ++++++++ crates/theme2/src/themes/ayu_dark.rs | 131 +++++++ crates/theme2/src/themes/ayu_light.rs | 131 +++++++ crates/theme2/src/themes/ayu_mirage.rs | 131 +++++++ crates/theme2/src/themes/gruvbox_dark.rs | 132 +++++++ crates/theme2/src/themes/gruvbox_dark_hard.rs | 132 +++++++ crates/theme2/src/themes/gruvbox_dark_soft.rs | 132 +++++++ crates/theme2/src/themes/gruvbox_light.rs | 132 +++++++ .../theme2/src/themes/gruvbox_light_hard.rs | 132 +++++++ .../theme2/src/themes/gruvbox_light_soft.rs | 132 +++++++ crates/theme2/src/themes/mod.rs | 79 ++++- crates/theme2/src/themes/one_dark.rs | 65 ++-- crates/theme2/src/themes/one_light.rs | 132 +++++++ crates/theme2/src/themes/rose_pine.rs | 331 ++---------------- crates/theme2/src/themes/rose_pine_dawn.rs | 133 +++++++ crates/theme2/src/themes/rose_pine_moon.rs | 133 +++++++ crates/theme2/src/themes/sandcastle.rs | 57 +-- crates/theme2/src/themes/solarized_dark.rs | 131 +++++++ crates/theme2/src/themes/solarized_light.rs | 131 +++++++ crates/theme2/src/themes/summercamp.rs | 131 +++++++ 41 files changed, 5062 insertions(+), 366 deletions(-) create mode 100644 crates/theme2/src/themes/andromeda.rs create mode 100644 crates/theme2/src/themes/atelier_cave_dark.rs create mode 100644 crates/theme2/src/themes/atelier_cave_light.rs create mode 100644 crates/theme2/src/themes/atelier_dune_dark.rs create mode 100644 crates/theme2/src/themes/atelier_dune_light.rs create mode 100644 crates/theme2/src/themes/atelier_estuary_dark.rs create mode 100644 crates/theme2/src/themes/atelier_estuary_light.rs create mode 100644 crates/theme2/src/themes/atelier_forest_dark.rs create mode 100644 crates/theme2/src/themes/atelier_forest_light.rs create mode 100644 crates/theme2/src/themes/atelier_heath_dark.rs create mode 100644 crates/theme2/src/themes/atelier_heath_light.rs create mode 100644 crates/theme2/src/themes/atelier_lakeside_dark.rs create mode 100644 crates/theme2/src/themes/atelier_lakeside_light.rs create mode 100644 crates/theme2/src/themes/atelier_plateau_dark.rs create mode 100644 crates/theme2/src/themes/atelier_plateau_light.rs create mode 100644 crates/theme2/src/themes/atelier_savanna_dark.rs create mode 100644 crates/theme2/src/themes/atelier_savanna_light.rs create mode 100644 crates/theme2/src/themes/atelier_seaside_dark.rs create mode 100644 crates/theme2/src/themes/atelier_seaside_light.rs create mode 100644 crates/theme2/src/themes/atelier_sulphurpool_dark.rs create mode 100644 crates/theme2/src/themes/atelier_sulphurpool_light.rs create mode 100644 crates/theme2/src/themes/ayu_dark.rs create mode 100644 crates/theme2/src/themes/ayu_light.rs create mode 100644 crates/theme2/src/themes/ayu_mirage.rs create mode 100644 crates/theme2/src/themes/gruvbox_dark.rs create mode 100644 crates/theme2/src/themes/gruvbox_dark_hard.rs create mode 100644 crates/theme2/src/themes/gruvbox_dark_soft.rs create mode 100644 crates/theme2/src/themes/gruvbox_light.rs create mode 100644 crates/theme2/src/themes/gruvbox_light_hard.rs create mode 100644 crates/theme2/src/themes/gruvbox_light_soft.rs create mode 100644 crates/theme2/src/themes/one_light.rs create mode 100644 crates/theme2/src/themes/rose_pine_dawn.rs create mode 100644 crates/theme2/src/themes/rose_pine_moon.rs create mode 100644 crates/theme2/src/themes/solarized_dark.rs create mode 100644 crates/theme2/src/themes/solarized_light.rs create mode 100644 crates/theme2/src/themes/summercamp.rs diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index b98a4db722..eec82ef5a7 100644 --- a/crates/theme2/src/registry.rs +++ b/crates/theme2/src/registry.rs @@ -1,7 +1,4 @@ -use crate::{ - themes::{one_dark, rose_pine, rose_pine_dawn, rose_pine_moon, sandcastle}, - Theme, ThemeMetadata, -}; +use crate::{themes, Theme, ThemeMetadata}; use anyhow::{anyhow, Result}; use gpui2::SharedString; use std::{collections::HashMap, sync::Arc}; @@ -41,11 +38,45 @@ impl Default for ThemeRegistry { }; this.insert_themes([ - one_dark(), - rose_pine(), - rose_pine_dawn(), - rose_pine_moon(), - sandcastle(), + themes::andromeda(), + themes::atelier_cave_dark(), + themes::atelier_cave_light(), + themes::atelier_dune_dark(), + themes::atelier_dune_light(), + themes::atelier_estuary_dark(), + themes::atelier_estuary_light(), + themes::atelier_forest_dark(), + themes::atelier_forest_light(), + themes::atelier_heath_dark(), + themes::atelier_heath_light(), + themes::atelier_lakeside_dark(), + themes::atelier_lakeside_light(), + themes::atelier_plateau_dark(), + themes::atelier_plateau_light(), + themes::atelier_savanna_dark(), + themes::atelier_savanna_light(), + themes::atelier_seaside_dark(), + themes::atelier_seaside_light(), + themes::atelier_sulphurpool_dark(), + themes::atelier_sulphurpool_light(), + themes::ayu_dark(), + themes::ayu_light(), + themes::ayu_mirage(), + themes::gruvbox_dark(), + themes::gruvbox_dark_hard(), + themes::gruvbox_dark_soft(), + themes::gruvbox_light(), + themes::gruvbox_light_hard(), + themes::gruvbox_light_soft(), + themes::one_dark(), + themes::one_light(), + themes::rose_pine(), + themes::rose_pine_dawn(), + themes::rose_pine_moon(), + themes::sandcastle(), + themes::solarized_dark(), + themes::solarized_light(), + themes::summercamp(), ]); this diff --git a/crates/theme2/src/themes/andromeda.rs b/crates/theme2/src/themes/andromeda.rs new file mode 100644 index 0000000000..b5cabfedfa --- /dev/null +++ b/crates/theme2/src/themes/andromeda.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn andromeda() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Andromeda".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x2b2f38ff).into(), + border_variant: rgba(0x2b2f38ff).into(), + border_focused: rgba(0x183934ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x262933ff).into(), + surface: rgba(0x21242bff).into(), + background: rgba(0x262933ff).into(), + filled_element: rgba(0x262933ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x12231fff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x12231fff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf7f7f8ff).into(), + text_muted: rgba(0xaca8aeff).into(), + text_placeholder: rgba(0xf82871ff).into(), + text_disabled: rgba(0x6b6b73ff).into(), + text_accent: rgba(0x10a793ff).into(), + icon_muted: rgba(0xaca8aeff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("emphasis".into(), rgba(0x10a793ff).into()), + ("punctuation.bracket".into(), rgba(0xd8d5dbff).into()), + ("attribute".into(), rgba(0x10a793ff).into()), + ("variable".into(), rgba(0xf7f7f8ff).into()), + ("predictive".into(), rgba(0x315f70ff).into()), + ("property".into(), rgba(0x10a793ff).into()), + ("variant".into(), rgba(0x10a793ff).into()), + ("embedded".into(), rgba(0xf7f7f8ff).into()), + ("string.special".into(), rgba(0xf29c14ff).into()), + ("keyword".into(), rgba(0x10a793ff).into()), + ("tag".into(), rgba(0x10a793ff).into()), + ("enum".into(), rgba(0xf29c14ff).into()), + ("link_text".into(), rgba(0xf29c14ff).into()), + ("primary".into(), rgba(0xf7f7f8ff).into()), + ("punctuation".into(), rgba(0xd8d5dbff).into()), + ("punctuation.special".into(), rgba(0xd8d5dbff).into()), + ("function".into(), rgba(0xfee56cff).into()), + ("number".into(), rgba(0x96df71ff).into()), + ("preproc".into(), rgba(0xf7f7f8ff).into()), + ("operator".into(), rgba(0xf29c14ff).into()), + ("constructor".into(), rgba(0x10a793ff).into()), + ("string.escape".into(), rgba(0xafabb1ff).into()), + ("string.special.symbol".into(), rgba(0xf29c14ff).into()), + ("string".into(), rgba(0xf29c14ff).into()), + ("comment".into(), rgba(0xafabb1ff).into()), + ("hint".into(), rgba(0x618399ff).into()), + ("type".into(), rgba(0x08e7c5ff).into()), + ("label".into(), rgba(0x10a793ff).into()), + ("comment.doc".into(), rgba(0xafabb1ff).into()), + ("text.literal".into(), rgba(0xf29c14ff).into()), + ("constant".into(), rgba(0x96df71ff).into()), + ("string.regex".into(), rgba(0xf29c14ff).into()), + ("emphasis.strong".into(), rgba(0x10a793ff).into()), + ("title".into(), rgba(0xf7f7f8ff).into()), + ("punctuation.delimiter".into(), rgba(0xd8d5dbff).into()), + ("link_uri".into(), rgba(0x96df71ff).into()), + ("boolean".into(), rgba(0x96df71ff).into()), + ("punctuation.list_marker".into(), rgba(0xd8d5dbff).into()), + ], + }, + status_bar: rgba(0x262933ff).into(), + title_bar: rgba(0x262933ff).into(), + toolbar: rgba(0x1e2025ff).into(), + tab_bar: rgba(0x21242bff).into(), + editor: rgba(0x1e2025ff).into(), + editor_subheader: rgba(0x21242bff).into(), + editor_active_line: rgba(0x21242bff).into(), + terminal: rgba(0x1e2025ff).into(), + image_fallback_background: rgba(0x262933ff).into(), + git_created: rgba(0x96df71ff).into(), + git_modified: rgba(0x10a793ff).into(), + git_deleted: rgba(0xf82871ff).into(), + git_conflict: rgba(0xfee56cff).into(), + git_ignored: rgba(0x6b6b73ff).into(), + git_renamed: rgba(0xfee56cff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x10a793ff).into(), + selection: rgba(0x10a7933d).into(), + }, + PlayerTheme { + cursor: rgba(0x96df71ff).into(), + selection: rgba(0x96df713d).into(), + }, + PlayerTheme { + cursor: rgba(0xc74cecff).into(), + selection: rgba(0xc74cec3d).into(), + }, + PlayerTheme { + cursor: rgba(0xf29c14ff).into(), + selection: rgba(0xf29c143d).into(), + }, + PlayerTheme { + cursor: rgba(0x893ea6ff).into(), + selection: rgba(0x893ea63d).into(), + }, + PlayerTheme { + cursor: rgba(0x08e7c5ff).into(), + selection: rgba(0x08e7c53d).into(), + }, + PlayerTheme { + cursor: rgba(0xf82871ff).into(), + selection: rgba(0xf828713d).into(), + }, + PlayerTheme { + cursor: rgba(0xfee56cff).into(), + selection: rgba(0xfee56c3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_cave_dark.rs b/crates/theme2/src/themes/atelier_cave_dark.rs new file mode 100644 index 0000000000..e3926e6d36 --- /dev/null +++ b/crates/theme2/src/themes/atelier_cave_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_cave_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Cave Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x56505eff).into(), + border_variant: rgba(0x56505eff).into(), + border_focused: rgba(0x222953ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x3a353fff).into(), + surface: rgba(0x221f26ff).into(), + background: rgba(0x3a353fff).into(), + filled_element: rgba(0x3a353fff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x161a35ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x161a35ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xefecf4ff).into(), + text_muted: rgba(0x898591ff).into(), + text_placeholder: rgba(0xbe4677ff).into(), + text_disabled: rgba(0x756f7eff).into(), + text_accent: rgba(0x566ddaff).into(), + icon_muted: rgba(0x898591ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("comment.doc".into(), rgba(0x8b8792ff).into()), + ("tag".into(), rgba(0x566ddaff).into()), + ("link_text".into(), rgba(0xaa563bff).into()), + ("constructor".into(), rgba(0x566ddaff).into()), + ("punctuation".into(), rgba(0xe2dfe7ff).into()), + ("punctuation.special".into(), rgba(0xbf3fbfff).into()), + ("string.special.symbol".into(), rgba(0x299292ff).into()), + ("string.escape".into(), rgba(0x8b8792ff).into()), + ("emphasis".into(), rgba(0x566ddaff).into()), + ("type".into(), rgba(0xa06d3aff).into()), + ("punctuation.delimiter".into(), rgba(0x8b8792ff).into()), + ("variant".into(), rgba(0xa06d3aff).into()), + ("variable.special".into(), rgba(0x9559e7ff).into()), + ("text.literal".into(), rgba(0xaa563bff).into()), + ("punctuation.list_marker".into(), rgba(0xe2dfe7ff).into()), + ("comment".into(), rgba(0x655f6dff).into()), + ("function.method".into(), rgba(0x576cdbff).into()), + ("property".into(), rgba(0xbe4677ff).into()), + ("operator".into(), rgba(0x8b8792ff).into()), + ("emphasis.strong".into(), rgba(0x566ddaff).into()), + ("label".into(), rgba(0x566ddaff).into()), + ("enum".into(), rgba(0xaa563bff).into()), + ("number".into(), rgba(0xaa563bff).into()), + ("primary".into(), rgba(0xe2dfe7ff).into()), + ("keyword".into(), rgba(0x9559e7ff).into()), + ( + "function.special.definition".into(), + rgba(0xa06d3aff).into(), + ), + ("punctuation.bracket".into(), rgba(0x8b8792ff).into()), + ("constant".into(), rgba(0x2b9292ff).into()), + ("string.special".into(), rgba(0xbf3fbfff).into()), + ("title".into(), rgba(0xefecf4ff).into()), + ("preproc".into(), rgba(0xefecf4ff).into()), + ("link_uri".into(), rgba(0x2b9292ff).into()), + ("string".into(), rgba(0x299292ff).into()), + ("embedded".into(), rgba(0xefecf4ff).into()), + ("hint".into(), rgba(0x706897ff).into()), + ("boolean".into(), rgba(0x2b9292ff).into()), + ("variable".into(), rgba(0xe2dfe7ff).into()), + ("predictive".into(), rgba(0x615787ff).into()), + ("string.regex".into(), rgba(0x388bc6ff).into()), + ("function".into(), rgba(0x576cdbff).into()), + ("attribute".into(), rgba(0x566ddaff).into()), + ], + }, + status_bar: rgba(0x3a353fff).into(), + title_bar: rgba(0x3a353fff).into(), + toolbar: rgba(0x19171cff).into(), + tab_bar: rgba(0x221f26ff).into(), + editor: rgba(0x19171cff).into(), + editor_subheader: rgba(0x221f26ff).into(), + editor_active_line: rgba(0x221f26ff).into(), + terminal: rgba(0x19171cff).into(), + image_fallback_background: rgba(0x3a353fff).into(), + git_created: rgba(0x2b9292ff).into(), + git_modified: rgba(0x566ddaff).into(), + git_deleted: rgba(0xbe4677ff).into(), + git_conflict: rgba(0xa06d3aff).into(), + git_ignored: rgba(0x756f7eff).into(), + git_renamed: rgba(0xa06d3aff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x566ddaff).into(), + selection: rgba(0x566dda3d).into(), + }, + PlayerTheme { + cursor: rgba(0x2b9292ff).into(), + selection: rgba(0x2b92923d).into(), + }, + PlayerTheme { + cursor: rgba(0xbf41bfff).into(), + selection: rgba(0xbf41bf3d).into(), + }, + PlayerTheme { + cursor: rgba(0xaa563bff).into(), + selection: rgba(0xaa563b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x955ae6ff).into(), + selection: rgba(0x955ae63d).into(), + }, + PlayerTheme { + cursor: rgba(0x3a8bc6ff).into(), + selection: rgba(0x3a8bc63d).into(), + }, + PlayerTheme { + cursor: rgba(0xbe4677ff).into(), + selection: rgba(0xbe46773d).into(), + }, + PlayerTheme { + cursor: rgba(0xa06d3aff).into(), + selection: rgba(0xa06d3a3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_cave_light.rs b/crates/theme2/src/themes/atelier_cave_light.rs new file mode 100644 index 0000000000..e21dd12c4a --- /dev/null +++ b/crates/theme2/src/themes/atelier_cave_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_cave_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Cave Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x8f8b96ff).into(), + border_variant: rgba(0x8f8b96ff).into(), + border_focused: rgba(0xc8c7f2ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xbfbcc5ff).into(), + surface: rgba(0xe6e3ebff).into(), + background: rgba(0xbfbcc5ff).into(), + filled_element: rgba(0xbfbcc5ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xe1e0f9ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xe1e0f9ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x19171cff).into(), + text_muted: rgba(0x5a5462ff).into(), + text_placeholder: rgba(0xbd4677ff).into(), + text_disabled: rgba(0x6e6876ff).into(), + text_accent: rgba(0x586cdaff).into(), + icon_muted: rgba(0x5a5462ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("link_text".into(), rgba(0xaa573cff).into()), + ("string".into(), rgba(0x299292ff).into()), + ("emphasis".into(), rgba(0x586cdaff).into()), + ("label".into(), rgba(0x586cdaff).into()), + ("property".into(), rgba(0xbe4677ff).into()), + ("emphasis.strong".into(), rgba(0x586cdaff).into()), + ("constant".into(), rgba(0x2b9292ff).into()), + ( + "function.special.definition".into(), + rgba(0xa06d3aff).into(), + ), + ("embedded".into(), rgba(0x19171cff).into()), + ("punctuation.special".into(), rgba(0xbf3fbfff).into()), + ("function".into(), rgba(0x576cdbff).into()), + ("tag".into(), rgba(0x586cdaff).into()), + ("number".into(), rgba(0xaa563bff).into()), + ("primary".into(), rgba(0x26232aff).into()), + ("text.literal".into(), rgba(0xaa573cff).into()), + ("variant".into(), rgba(0xa06d3aff).into()), + ("type".into(), rgba(0xa06d3aff).into()), + ("punctuation".into(), rgba(0x26232aff).into()), + ("string.escape".into(), rgba(0x585260ff).into()), + ("keyword".into(), rgba(0x9559e7ff).into()), + ("title".into(), rgba(0x19171cff).into()), + ("constructor".into(), rgba(0x586cdaff).into()), + ("punctuation.list_marker".into(), rgba(0x26232aff).into()), + ("string.special".into(), rgba(0xbf3fbfff).into()), + ("operator".into(), rgba(0x585260ff).into()), + ("function.method".into(), rgba(0x576cdbff).into()), + ("link_uri".into(), rgba(0x2b9292ff).into()), + ("variable.special".into(), rgba(0x9559e7ff).into()), + ("hint".into(), rgba(0x776d9dff).into()), + ("punctuation.bracket".into(), rgba(0x585260ff).into()), + ("string.special.symbol".into(), rgba(0x299292ff).into()), + ("predictive".into(), rgba(0x887fafff).into()), + ("attribute".into(), rgba(0x586cdaff).into()), + ("enum".into(), rgba(0xaa573cff).into()), + ("preproc".into(), rgba(0x19171cff).into()), + ("boolean".into(), rgba(0x2b9292ff).into()), + ("variable".into(), rgba(0x26232aff).into()), + ("comment.doc".into(), rgba(0x585260ff).into()), + ("string.regex".into(), rgba(0x388bc6ff).into()), + ("punctuation.delimiter".into(), rgba(0x585260ff).into()), + ("comment".into(), rgba(0x7d7787ff).into()), + ], + }, + status_bar: rgba(0xbfbcc5ff).into(), + title_bar: rgba(0xbfbcc5ff).into(), + toolbar: rgba(0xefecf4ff).into(), + tab_bar: rgba(0xe6e3ebff).into(), + editor: rgba(0xefecf4ff).into(), + editor_subheader: rgba(0xe6e3ebff).into(), + editor_active_line: rgba(0xe6e3ebff).into(), + terminal: rgba(0xefecf4ff).into(), + image_fallback_background: rgba(0xbfbcc5ff).into(), + git_created: rgba(0x2b9292ff).into(), + git_modified: rgba(0x586cdaff).into(), + git_deleted: rgba(0xbd4677ff).into(), + git_conflict: rgba(0xa06e3bff).into(), + git_ignored: rgba(0x6e6876ff).into(), + git_renamed: rgba(0xa06e3bff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x586cdaff).into(), + selection: rgba(0x586cda3d).into(), + }, + PlayerTheme { + cursor: rgba(0x2b9292ff).into(), + selection: rgba(0x2b92923d).into(), + }, + PlayerTheme { + cursor: rgba(0xbf41bfff).into(), + selection: rgba(0xbf41bf3d).into(), + }, + PlayerTheme { + cursor: rgba(0xaa573cff).into(), + selection: rgba(0xaa573c3d).into(), + }, + PlayerTheme { + cursor: rgba(0x955ae6ff).into(), + selection: rgba(0x955ae63d).into(), + }, + PlayerTheme { + cursor: rgba(0x3a8bc6ff).into(), + selection: rgba(0x3a8bc63d).into(), + }, + PlayerTheme { + cursor: rgba(0xbd4677ff).into(), + selection: rgba(0xbd46773d).into(), + }, + PlayerTheme { + cursor: rgba(0xa06e3bff).into(), + selection: rgba(0xa06e3b3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_dune_dark.rs b/crates/theme2/src/themes/atelier_dune_dark.rs new file mode 100644 index 0000000000..313b4df261 --- /dev/null +++ b/crates/theme2/src/themes/atelier_dune_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_dune_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Dune Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x6c695cff).into(), + border_variant: rgba(0x6c695cff).into(), + border_focused: rgba(0x262f56ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x45433bff).into(), + surface: rgba(0x262622ff).into(), + background: rgba(0x45433bff).into(), + filled_element: rgba(0x45433bff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x171e38ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x171e38ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfefbecff).into(), + text_muted: rgba(0xa4a08bff).into(), + text_placeholder: rgba(0xd73837ff).into(), + text_disabled: rgba(0x8f8b77ff).into(), + text_accent: rgba(0x6684e0ff).into(), + icon_muted: rgba(0xa4a08bff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("constructor".into(), rgba(0x6684e0ff).into()), + ("punctuation".into(), rgba(0xe8e4cfff).into()), + ("punctuation.delimiter".into(), rgba(0xa6a28cff).into()), + ("string.special".into(), rgba(0xd43451ff).into()), + ("string.escape".into(), rgba(0xa6a28cff).into()), + ("comment".into(), rgba(0x7d7a68ff).into()), + ("enum".into(), rgba(0xb65611ff).into()), + ("variable.special".into(), rgba(0xb854d4ff).into()), + ("primary".into(), rgba(0xe8e4cfff).into()), + ("comment.doc".into(), rgba(0xa6a28cff).into()), + ("label".into(), rgba(0x6684e0ff).into()), + ("operator".into(), rgba(0xa6a28cff).into()), + ("string".into(), rgba(0x5fac38ff).into()), + ("variant".into(), rgba(0xae9512ff).into()), + ("variable".into(), rgba(0xe8e4cfff).into()), + ("function.method".into(), rgba(0x6583e1ff).into()), + ( + "function.special.definition".into(), + rgba(0xae9512ff).into(), + ), + ("string.regex".into(), rgba(0x1ead82ff).into()), + ("emphasis.strong".into(), rgba(0x6684e0ff).into()), + ("punctuation.special".into(), rgba(0xd43451ff).into()), + ("punctuation.bracket".into(), rgba(0xa6a28cff).into()), + ("link_text".into(), rgba(0xb65611ff).into()), + ("link_uri".into(), rgba(0x5fac39ff).into()), + ("boolean".into(), rgba(0x5fac39ff).into()), + ("hint".into(), rgba(0xb17272ff).into()), + ("tag".into(), rgba(0x6684e0ff).into()), + ("function".into(), rgba(0x6583e1ff).into()), + ("title".into(), rgba(0xfefbecff).into()), + ("property".into(), rgba(0xd73737ff).into()), + ("type".into(), rgba(0xae9512ff).into()), + ("constant".into(), rgba(0x5fac39ff).into()), + ("attribute".into(), rgba(0x6684e0ff).into()), + ("predictive".into(), rgba(0x9c6262ff).into()), + ("string.special.symbol".into(), rgba(0x5fac38ff).into()), + ("punctuation.list_marker".into(), rgba(0xe8e4cfff).into()), + ("emphasis".into(), rgba(0x6684e0ff).into()), + ("keyword".into(), rgba(0xb854d4ff).into()), + ("text.literal".into(), rgba(0xb65611ff).into()), + ("number".into(), rgba(0xb65610ff).into()), + ("preproc".into(), rgba(0xfefbecff).into()), + ("embedded".into(), rgba(0xfefbecff).into()), + ], + }, + status_bar: rgba(0x45433bff).into(), + title_bar: rgba(0x45433bff).into(), + toolbar: rgba(0x20201dff).into(), + tab_bar: rgba(0x262622ff).into(), + editor: rgba(0x20201dff).into(), + editor_subheader: rgba(0x262622ff).into(), + editor_active_line: rgba(0x262622ff).into(), + terminal: rgba(0x20201dff).into(), + image_fallback_background: rgba(0x45433bff).into(), + git_created: rgba(0x5fac39ff).into(), + git_modified: rgba(0x6684e0ff).into(), + git_deleted: rgba(0xd73837ff).into(), + git_conflict: rgba(0xae9414ff).into(), + git_ignored: rgba(0x8f8b77ff).into(), + git_renamed: rgba(0xae9414ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x6684e0ff).into(), + selection: rgba(0x6684e03d).into(), + }, + PlayerTheme { + cursor: rgba(0x5fac39ff).into(), + selection: rgba(0x5fac393d).into(), + }, + PlayerTheme { + cursor: rgba(0xd43651ff).into(), + selection: rgba(0xd436513d).into(), + }, + PlayerTheme { + cursor: rgba(0xb65611ff).into(), + selection: rgba(0xb656113d).into(), + }, + PlayerTheme { + cursor: rgba(0xb854d3ff).into(), + selection: rgba(0xb854d33d).into(), + }, + PlayerTheme { + cursor: rgba(0x20ad83ff).into(), + selection: rgba(0x20ad833d).into(), + }, + PlayerTheme { + cursor: rgba(0xd73837ff).into(), + selection: rgba(0xd738373d).into(), + }, + PlayerTheme { + cursor: rgba(0xae9414ff).into(), + selection: rgba(0xae94143d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_dune_light.rs b/crates/theme2/src/themes/atelier_dune_light.rs new file mode 100644 index 0000000000..c7dfd884cf --- /dev/null +++ b/crates/theme2/src/themes/atelier_dune_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_dune_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Dune Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xa8a48eff).into(), + border_variant: rgba(0xa8a48eff).into(), + border_focused: rgba(0xcdd1f5ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xcecab4ff).into(), + surface: rgba(0xeeebd7ff).into(), + background: rgba(0xcecab4ff).into(), + filled_element: rgba(0xcecab4ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xe3e5faff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xe3e5faff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x20201dff).into(), + text_muted: rgba(0x706d5fff).into(), + text_placeholder: rgba(0xd73737ff).into(), + text_disabled: rgba(0x878471ff).into(), + text_accent: rgba(0x6684dfff).into(), + icon_muted: rgba(0x706d5fff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("primary".into(), rgba(0x292824ff).into()), + ("comment".into(), rgba(0x999580ff).into()), + ("type".into(), rgba(0xae9512ff).into()), + ("variant".into(), rgba(0xae9512ff).into()), + ("label".into(), rgba(0x6684dfff).into()), + ("function.method".into(), rgba(0x6583e1ff).into()), + ("variable.special".into(), rgba(0xb854d4ff).into()), + ("string.regex".into(), rgba(0x1ead82ff).into()), + ("property".into(), rgba(0xd73737ff).into()), + ("keyword".into(), rgba(0xb854d4ff).into()), + ("number".into(), rgba(0xb65610ff).into()), + ("punctuation.list_marker".into(), rgba(0x292824ff).into()), + ( + "function.special.definition".into(), + rgba(0xae9512ff).into(), + ), + ("punctuation.special".into(), rgba(0xd43451ff).into()), + ("punctuation".into(), rgba(0x292824ff).into()), + ("punctuation.delimiter".into(), rgba(0x6e6b5eff).into()), + ("tag".into(), rgba(0x6684dfff).into()), + ("link_text".into(), rgba(0xb65712ff).into()), + ("boolean".into(), rgba(0x61ac39ff).into()), + ("hint".into(), rgba(0xb37979ff).into()), + ("operator".into(), rgba(0x6e6b5eff).into()), + ("constant".into(), rgba(0x61ac39ff).into()), + ("function".into(), rgba(0x6583e1ff).into()), + ("text.literal".into(), rgba(0xb65712ff).into()), + ("string.special.symbol".into(), rgba(0x5fac38ff).into()), + ("attribute".into(), rgba(0x6684dfff).into()), + ("emphasis".into(), rgba(0x6684dfff).into()), + ("preproc".into(), rgba(0x20201dff).into()), + ("comment.doc".into(), rgba(0x6e6b5eff).into()), + ("punctuation.bracket".into(), rgba(0x6e6b5eff).into()), + ("string".into(), rgba(0x5fac38ff).into()), + ("enum".into(), rgba(0xb65712ff).into()), + ("variable".into(), rgba(0x292824ff).into()), + ("string.special".into(), rgba(0xd43451ff).into()), + ("embedded".into(), rgba(0x20201dff).into()), + ("emphasis.strong".into(), rgba(0x6684dfff).into()), + ("predictive".into(), rgba(0xc88a8aff).into()), + ("title".into(), rgba(0x20201dff).into()), + ("constructor".into(), rgba(0x6684dfff).into()), + ("link_uri".into(), rgba(0x61ac39ff).into()), + ("string.escape".into(), rgba(0x6e6b5eff).into()), + ], + }, + status_bar: rgba(0xcecab4ff).into(), + title_bar: rgba(0xcecab4ff).into(), + toolbar: rgba(0xfefbecff).into(), + tab_bar: rgba(0xeeebd7ff).into(), + editor: rgba(0xfefbecff).into(), + editor_subheader: rgba(0xeeebd7ff).into(), + editor_active_line: rgba(0xeeebd7ff).into(), + terminal: rgba(0xfefbecff).into(), + image_fallback_background: rgba(0xcecab4ff).into(), + git_created: rgba(0x61ac39ff).into(), + git_modified: rgba(0x6684dfff).into(), + git_deleted: rgba(0xd73737ff).into(), + git_conflict: rgba(0xae9414ff).into(), + git_ignored: rgba(0x878471ff).into(), + git_renamed: rgba(0xae9414ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x6684dfff).into(), + selection: rgba(0x6684df3d).into(), + }, + PlayerTheme { + cursor: rgba(0x61ac39ff).into(), + selection: rgba(0x61ac393d).into(), + }, + PlayerTheme { + cursor: rgba(0xd43652ff).into(), + selection: rgba(0xd436523d).into(), + }, + PlayerTheme { + cursor: rgba(0xb65712ff).into(), + selection: rgba(0xb657123d).into(), + }, + PlayerTheme { + cursor: rgba(0xb755d3ff).into(), + selection: rgba(0xb755d33d).into(), + }, + PlayerTheme { + cursor: rgba(0x21ad82ff).into(), + selection: rgba(0x21ad823d).into(), + }, + PlayerTheme { + cursor: rgba(0xd73737ff).into(), + selection: rgba(0xd737373d).into(), + }, + PlayerTheme { + cursor: rgba(0xae9414ff).into(), + selection: rgba(0xae94143d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_estuary_dark.rs b/crates/theme2/src/themes/atelier_estuary_dark.rs new file mode 100644 index 0000000000..9dfa5d37a9 --- /dev/null +++ b/crates/theme2/src/themes/atelier_estuary_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_estuary_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Estuary Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x5d5c4cff).into(), + border_variant: rgba(0x5d5c4cff).into(), + border_focused: rgba(0x1c3927ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x424136ff).into(), + surface: rgba(0x2c2b23ff).into(), + background: rgba(0x424136ff).into(), + filled_element: rgba(0x424136ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x142319ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x142319ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf4f3ecff).into(), + text_muted: rgba(0x91907fff).into(), + text_placeholder: rgba(0xba6136ff).into(), + text_disabled: rgba(0x7d7c6aff).into(), + text_accent: rgba(0x36a165ff).into(), + icon_muted: rgba(0x91907fff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.special.symbol".into(), rgba(0x7c9725ff).into()), + ("comment".into(), rgba(0x6c6b5aff).into()), + ("operator".into(), rgba(0x929181ff).into()), + ("punctuation.delimiter".into(), rgba(0x929181ff).into()), + ("keyword".into(), rgba(0x5f9182ff).into()), + ("punctuation.special".into(), rgba(0x9d6b7bff).into()), + ("preproc".into(), rgba(0xf4f3ecff).into()), + ("title".into(), rgba(0xf4f3ecff).into()), + ("string.escape".into(), rgba(0x929181ff).into()), + ("boolean".into(), rgba(0x7d9726ff).into()), + ("punctuation.bracket".into(), rgba(0x929181ff).into()), + ("emphasis.strong".into(), rgba(0x36a165ff).into()), + ("string".into(), rgba(0x7c9725ff).into()), + ("constant".into(), rgba(0x7d9726ff).into()), + ("link_text".into(), rgba(0xae7214ff).into()), + ("tag".into(), rgba(0x36a165ff).into()), + ("hint".into(), rgba(0x6f815aff).into()), + ("punctuation".into(), rgba(0xe7e6dfff).into()), + ("string.regex".into(), rgba(0x5a9d47ff).into()), + ("variant".into(), rgba(0xa5980cff).into()), + ("type".into(), rgba(0xa5980cff).into()), + ("attribute".into(), rgba(0x36a165ff).into()), + ("emphasis".into(), rgba(0x36a165ff).into()), + ("enum".into(), rgba(0xae7214ff).into()), + ("number".into(), rgba(0xae7312ff).into()), + ("property".into(), rgba(0xba6135ff).into()), + ("predictive".into(), rgba(0x5f724cff).into()), + ( + "function.special.definition".into(), + rgba(0xa5980cff).into(), + ), + ("link_uri".into(), rgba(0x7d9726ff).into()), + ("variable.special".into(), rgba(0x5f9182ff).into()), + ("text.literal".into(), rgba(0xae7214ff).into()), + ("label".into(), rgba(0x36a165ff).into()), + ("primary".into(), rgba(0xe7e6dfff).into()), + ("variable".into(), rgba(0xe7e6dfff).into()), + ("embedded".into(), rgba(0xf4f3ecff).into()), + ("function.method".into(), rgba(0x35a166ff).into()), + ("comment.doc".into(), rgba(0x929181ff).into()), + ("string.special".into(), rgba(0x9d6b7bff).into()), + ("constructor".into(), rgba(0x36a165ff).into()), + ("punctuation.list_marker".into(), rgba(0xe7e6dfff).into()), + ("function".into(), rgba(0x35a166ff).into()), + ], + }, + status_bar: rgba(0x424136ff).into(), + title_bar: rgba(0x424136ff).into(), + toolbar: rgba(0x22221bff).into(), + tab_bar: rgba(0x2c2b23ff).into(), + editor: rgba(0x22221bff).into(), + editor_subheader: rgba(0x2c2b23ff).into(), + editor_active_line: rgba(0x2c2b23ff).into(), + terminal: rgba(0x22221bff).into(), + image_fallback_background: rgba(0x424136ff).into(), + git_created: rgba(0x7d9726ff).into(), + git_modified: rgba(0x36a165ff).into(), + git_deleted: rgba(0xba6136ff).into(), + git_conflict: rgba(0xa5980fff).into(), + git_ignored: rgba(0x7d7c6aff).into(), + git_renamed: rgba(0xa5980fff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x36a165ff).into(), + selection: rgba(0x36a1653d).into(), + }, + PlayerTheme { + cursor: rgba(0x7d9726ff).into(), + selection: rgba(0x7d97263d).into(), + }, + PlayerTheme { + cursor: rgba(0x9d6b7bff).into(), + selection: rgba(0x9d6b7b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xae7214ff).into(), + selection: rgba(0xae72143d).into(), + }, + PlayerTheme { + cursor: rgba(0x5f9182ff).into(), + selection: rgba(0x5f91823d).into(), + }, + PlayerTheme { + cursor: rgba(0x5a9d47ff).into(), + selection: rgba(0x5a9d473d).into(), + }, + PlayerTheme { + cursor: rgba(0xba6136ff).into(), + selection: rgba(0xba61363d).into(), + }, + PlayerTheme { + cursor: rgba(0xa5980fff).into(), + selection: rgba(0xa5980f3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_estuary_light.rs b/crates/theme2/src/themes/atelier_estuary_light.rs new file mode 100644 index 0000000000..9e79b6bce0 --- /dev/null +++ b/crates/theme2/src/themes/atelier_estuary_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_estuary_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Estuary Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x969585ff).into(), + border_variant: rgba(0x969585ff).into(), + border_focused: rgba(0xbbddc6ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xc5c4b9ff).into(), + surface: rgba(0xebeae3ff).into(), + background: rgba(0xc5c4b9ff).into(), + filled_element: rgba(0xc5c4b9ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xd9ecdfff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xd9ecdfff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x22221bff).into(), + text_muted: rgba(0x61604fff).into(), + text_placeholder: rgba(0xba6336ff).into(), + text_disabled: rgba(0x767463ff).into(), + text_accent: rgba(0x37a165ff).into(), + icon_muted: rgba(0x61604fff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.special".into(), rgba(0x9d6b7bff).into()), + ("link_text".into(), rgba(0xae7214ff).into()), + ("emphasis.strong".into(), rgba(0x37a165ff).into()), + ("tag".into(), rgba(0x37a165ff).into()), + ("primary".into(), rgba(0x302f27ff).into()), + ("emphasis".into(), rgba(0x37a165ff).into()), + ("hint".into(), rgba(0x758961ff).into()), + ("title".into(), rgba(0x22221bff).into()), + ("string.regex".into(), rgba(0x5a9d47ff).into()), + ("attribute".into(), rgba(0x37a165ff).into()), + ("string.escape".into(), rgba(0x5f5e4eff).into()), + ("embedded".into(), rgba(0x22221bff).into()), + ("punctuation.bracket".into(), rgba(0x5f5e4eff).into()), + ( + "function.special.definition".into(), + rgba(0xa5980cff).into(), + ), + ("operator".into(), rgba(0x5f5e4eff).into()), + ("constant".into(), rgba(0x7c9728ff).into()), + ("comment.doc".into(), rgba(0x5f5e4eff).into()), + ("label".into(), rgba(0x37a165ff).into()), + ("variable".into(), rgba(0x302f27ff).into()), + ("punctuation".into(), rgba(0x302f27ff).into()), + ("punctuation.delimiter".into(), rgba(0x5f5e4eff).into()), + ("comment".into(), rgba(0x878573ff).into()), + ("punctuation.special".into(), rgba(0x9d6b7bff).into()), + ("string.special.symbol".into(), rgba(0x7c9725ff).into()), + ("enum".into(), rgba(0xae7214ff).into()), + ("variable.special".into(), rgba(0x5f9182ff).into()), + ("link_uri".into(), rgba(0x7c9728ff).into()), + ("punctuation.list_marker".into(), rgba(0x302f27ff).into()), + ("number".into(), rgba(0xae7312ff).into()), + ("function".into(), rgba(0x35a166ff).into()), + ("text.literal".into(), rgba(0xae7214ff).into()), + ("boolean".into(), rgba(0x7c9728ff).into()), + ("predictive".into(), rgba(0x879a72ff).into()), + ("type".into(), rgba(0xa5980cff).into()), + ("constructor".into(), rgba(0x37a165ff).into()), + ("property".into(), rgba(0xba6135ff).into()), + ("keyword".into(), rgba(0x5f9182ff).into()), + ("function.method".into(), rgba(0x35a166ff).into()), + ("variant".into(), rgba(0xa5980cff).into()), + ("string".into(), rgba(0x7c9725ff).into()), + ("preproc".into(), rgba(0x22221bff).into()), + ], + }, + status_bar: rgba(0xc5c4b9ff).into(), + title_bar: rgba(0xc5c4b9ff).into(), + toolbar: rgba(0xf4f3ecff).into(), + tab_bar: rgba(0xebeae3ff).into(), + editor: rgba(0xf4f3ecff).into(), + editor_subheader: rgba(0xebeae3ff).into(), + editor_active_line: rgba(0xebeae3ff).into(), + terminal: rgba(0xf4f3ecff).into(), + image_fallback_background: rgba(0xc5c4b9ff).into(), + git_created: rgba(0x7c9728ff).into(), + git_modified: rgba(0x37a165ff).into(), + git_deleted: rgba(0xba6336ff).into(), + git_conflict: rgba(0xa5980fff).into(), + git_ignored: rgba(0x767463ff).into(), + git_renamed: rgba(0xa5980fff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x37a165ff).into(), + selection: rgba(0x37a1653d).into(), + }, + PlayerTheme { + cursor: rgba(0x7c9728ff).into(), + selection: rgba(0x7c97283d).into(), + }, + PlayerTheme { + cursor: rgba(0x9d6b7bff).into(), + selection: rgba(0x9d6b7b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xae7214ff).into(), + selection: rgba(0xae72143d).into(), + }, + PlayerTheme { + cursor: rgba(0x5f9182ff).into(), + selection: rgba(0x5f91823d).into(), + }, + PlayerTheme { + cursor: rgba(0x5c9d49ff).into(), + selection: rgba(0x5c9d493d).into(), + }, + PlayerTheme { + cursor: rgba(0xba6336ff).into(), + selection: rgba(0xba63363d).into(), + }, + PlayerTheme { + cursor: rgba(0xa5980fff).into(), + selection: rgba(0xa5980f3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_forest_dark.rs b/crates/theme2/src/themes/atelier_forest_dark.rs new file mode 100644 index 0000000000..2e8cef56de --- /dev/null +++ b/crates/theme2/src/themes/atelier_forest_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_forest_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Forest Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x665f5cff).into(), + border_variant: rgba(0x665f5cff).into(), + border_focused: rgba(0x182d5bff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x443c39ff).into(), + surface: rgba(0x27211eff).into(), + background: rgba(0x443c39ff).into(), + filled_element: rgba(0x443c39ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x0f1c3dff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x0f1c3dff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf0eeedff).into(), + text_muted: rgba(0xa79f9dff).into(), + text_placeholder: rgba(0xf22c3fff).into(), + text_disabled: rgba(0x8e8683ff).into(), + text_accent: rgba(0x407ee6ff).into(), + icon_muted: rgba(0xa79f9dff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("link_uri".into(), rgba(0x7a9726ff).into()), + ("punctuation.list_marker".into(), rgba(0xe6e2e0ff).into()), + ("type".into(), rgba(0xc38417ff).into()), + ("punctuation.bracket".into(), rgba(0xa8a19fff).into()), + ("punctuation".into(), rgba(0xe6e2e0ff).into()), + ("preproc".into(), rgba(0xf0eeedff).into()), + ("punctuation.special".into(), rgba(0xc33ff3ff).into()), + ("variable.special".into(), rgba(0x6666eaff).into()), + ("tag".into(), rgba(0x407ee6ff).into()), + ("constructor".into(), rgba(0x407ee6ff).into()), + ("title".into(), rgba(0xf0eeedff).into()), + ("hint".into(), rgba(0xa77087ff).into()), + ("constant".into(), rgba(0x7a9726ff).into()), + ("number".into(), rgba(0xdf521fff).into()), + ("emphasis.strong".into(), rgba(0x407ee6ff).into()), + ("boolean".into(), rgba(0x7a9726ff).into()), + ("comment".into(), rgba(0x766e6bff).into()), + ("string.special".into(), rgba(0xc33ff3ff).into()), + ("text.literal".into(), rgba(0xdf5321ff).into()), + ("string.regex".into(), rgba(0x3c96b8ff).into()), + ("enum".into(), rgba(0xdf5321ff).into()), + ("operator".into(), rgba(0xa8a19fff).into()), + ("embedded".into(), rgba(0xf0eeedff).into()), + ("string.special.symbol".into(), rgba(0x7a9725ff).into()), + ("predictive".into(), rgba(0x8f5b70ff).into()), + ("comment.doc".into(), rgba(0xa8a19fff).into()), + ("variant".into(), rgba(0xc38417ff).into()), + ("label".into(), rgba(0x407ee6ff).into()), + ("property".into(), rgba(0xf22c40ff).into()), + ("keyword".into(), rgba(0x6666eaff).into()), + ("function".into(), rgba(0x3f7ee7ff).into()), + ("string.escape".into(), rgba(0xa8a19fff).into()), + ("string".into(), rgba(0x7a9725ff).into()), + ("primary".into(), rgba(0xe6e2e0ff).into()), + ("function.method".into(), rgba(0x3f7ee7ff).into()), + ("link_text".into(), rgba(0xdf5321ff).into()), + ("attribute".into(), rgba(0x407ee6ff).into()), + ("emphasis".into(), rgba(0x407ee6ff).into()), + ( + "function.special.definition".into(), + rgba(0xc38417ff).into(), + ), + ("variable".into(), rgba(0xe6e2e0ff).into()), + ("punctuation.delimiter".into(), rgba(0xa8a19fff).into()), + ], + }, + status_bar: rgba(0x443c39ff).into(), + title_bar: rgba(0x443c39ff).into(), + toolbar: rgba(0x1b1918ff).into(), + tab_bar: rgba(0x27211eff).into(), + editor: rgba(0x1b1918ff).into(), + editor_subheader: rgba(0x27211eff).into(), + editor_active_line: rgba(0x27211eff).into(), + terminal: rgba(0x1b1918ff).into(), + image_fallback_background: rgba(0x443c39ff).into(), + git_created: rgba(0x7a9726ff).into(), + git_modified: rgba(0x407ee6ff).into(), + git_deleted: rgba(0xf22c3fff).into(), + git_conflict: rgba(0xc38418ff).into(), + git_ignored: rgba(0x8e8683ff).into(), + git_renamed: rgba(0xc38418ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x407ee6ff).into(), + selection: rgba(0x407ee63d).into(), + }, + PlayerTheme { + cursor: rgba(0x7a9726ff).into(), + selection: rgba(0x7a97263d).into(), + }, + PlayerTheme { + cursor: rgba(0xc340f2ff).into(), + selection: rgba(0xc340f23d).into(), + }, + PlayerTheme { + cursor: rgba(0xdf5321ff).into(), + selection: rgba(0xdf53213d).into(), + }, + PlayerTheme { + cursor: rgba(0x6565e9ff).into(), + selection: rgba(0x6565e93d).into(), + }, + PlayerTheme { + cursor: rgba(0x3d97b8ff).into(), + selection: rgba(0x3d97b83d).into(), + }, + PlayerTheme { + cursor: rgba(0xf22c3fff).into(), + selection: rgba(0xf22c3f3d).into(), + }, + PlayerTheme { + cursor: rgba(0xc38418ff).into(), + selection: rgba(0xc384183d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_forest_light.rs b/crates/theme2/src/themes/atelier_forest_light.rs new file mode 100644 index 0000000000..94d525835d --- /dev/null +++ b/crates/theme2/src/themes/atelier_forest_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_forest_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Forest Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xaaa3a1ff).into(), + border_variant: rgba(0xaaa3a1ff).into(), + border_focused: rgba(0xc6cef7ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xccc7c5ff).into(), + surface: rgba(0xe9e6e4ff).into(), + background: rgba(0xccc7c5ff).into(), + filled_element: rgba(0xccc7c5ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xdfe3fbff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xdfe3fbff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x1b1918ff).into(), + text_muted: rgba(0x6a6360ff).into(), + text_placeholder: rgba(0xf22e40ff).into(), + text_disabled: rgba(0x837b78ff).into(), + text_accent: rgba(0x407ee6ff).into(), + icon_muted: rgba(0x6a6360ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("punctuation.special".into(), rgba(0xc33ff3ff).into()), + ("text.literal".into(), rgba(0xdf5421ff).into()), + ("string.escape".into(), rgba(0x68615eff).into()), + ("string.regex".into(), rgba(0x3c96b8ff).into()), + ("number".into(), rgba(0xdf521fff).into()), + ("preproc".into(), rgba(0x1b1918ff).into()), + ("keyword".into(), rgba(0x6666eaff).into()), + ("variable.special".into(), rgba(0x6666eaff).into()), + ("punctuation.delimiter".into(), rgba(0x68615eff).into()), + ("emphasis.strong".into(), rgba(0x407ee6ff).into()), + ("boolean".into(), rgba(0x7a9728ff).into()), + ("variant".into(), rgba(0xc38417ff).into()), + ("predictive".into(), rgba(0xbe899eff).into()), + ("tag".into(), rgba(0x407ee6ff).into()), + ("property".into(), rgba(0xf22c40ff).into()), + ("enum".into(), rgba(0xdf5421ff).into()), + ("attribute".into(), rgba(0x407ee6ff).into()), + ("function.method".into(), rgba(0x3f7ee7ff).into()), + ("function".into(), rgba(0x3f7ee7ff).into()), + ("emphasis".into(), rgba(0x407ee6ff).into()), + ("primary".into(), rgba(0x2c2421ff).into()), + ("variable".into(), rgba(0x2c2421ff).into()), + ("constant".into(), rgba(0x7a9728ff).into()), + ("title".into(), rgba(0x1b1918ff).into()), + ("comment.doc".into(), rgba(0x68615eff).into()), + ("constructor".into(), rgba(0x407ee6ff).into()), + ("type".into(), rgba(0xc38417ff).into()), + ("punctuation.list_marker".into(), rgba(0x2c2421ff).into()), + ("punctuation".into(), rgba(0x2c2421ff).into()), + ("string".into(), rgba(0x7a9725ff).into()), + ("label".into(), rgba(0x407ee6ff).into()), + ("string.special".into(), rgba(0xc33ff3ff).into()), + ("embedded".into(), rgba(0x1b1918ff).into()), + ("link_text".into(), rgba(0xdf5421ff).into()), + ("punctuation.bracket".into(), rgba(0x68615eff).into()), + ("comment".into(), rgba(0x9c9491ff).into()), + ( + "function.special.definition".into(), + rgba(0xc38417ff).into(), + ), + ("link_uri".into(), rgba(0x7a9728ff).into()), + ("operator".into(), rgba(0x68615eff).into()), + ("hint".into(), rgba(0xa67287ff).into()), + ("string.special.symbol".into(), rgba(0x7a9725ff).into()), + ], + }, + status_bar: rgba(0xccc7c5ff).into(), + title_bar: rgba(0xccc7c5ff).into(), + toolbar: rgba(0xf0eeedff).into(), + tab_bar: rgba(0xe9e6e4ff).into(), + editor: rgba(0xf0eeedff).into(), + editor_subheader: rgba(0xe9e6e4ff).into(), + editor_active_line: rgba(0xe9e6e4ff).into(), + terminal: rgba(0xf0eeedff).into(), + image_fallback_background: rgba(0xccc7c5ff).into(), + git_created: rgba(0x7a9728ff).into(), + git_modified: rgba(0x407ee6ff).into(), + git_deleted: rgba(0xf22e40ff).into(), + git_conflict: rgba(0xc38419ff).into(), + git_ignored: rgba(0x837b78ff).into(), + git_renamed: rgba(0xc38419ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x407ee6ff).into(), + selection: rgba(0x407ee63d).into(), + }, + PlayerTheme { + cursor: rgba(0x7a9728ff).into(), + selection: rgba(0x7a97283d).into(), + }, + PlayerTheme { + cursor: rgba(0xc340f2ff).into(), + selection: rgba(0xc340f23d).into(), + }, + PlayerTheme { + cursor: rgba(0xdf5421ff).into(), + selection: rgba(0xdf54213d).into(), + }, + PlayerTheme { + cursor: rgba(0x6765e9ff).into(), + selection: rgba(0x6765e93d).into(), + }, + PlayerTheme { + cursor: rgba(0x3e96b8ff).into(), + selection: rgba(0x3e96b83d).into(), + }, + PlayerTheme { + cursor: rgba(0xf22e40ff).into(), + selection: rgba(0xf22e403d).into(), + }, + PlayerTheme { + cursor: rgba(0xc38419ff).into(), + selection: rgba(0xc384193d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_heath_dark.rs b/crates/theme2/src/themes/atelier_heath_dark.rs new file mode 100644 index 0000000000..c7e9590689 --- /dev/null +++ b/crates/theme2/src/themes/atelier_heath_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_heath_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Heath Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x675b67ff).into(), + border_variant: rgba(0x675b67ff).into(), + border_focused: rgba(0x192961ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x433a43ff).into(), + surface: rgba(0x252025ff).into(), + background: rgba(0x433a43ff).into(), + filled_element: rgba(0x433a43ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x0d1a43ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x0d1a43ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf7f3f7ff).into(), + text_muted: rgba(0xa899a8ff).into(), + text_placeholder: rgba(0xca3f2bff).into(), + text_disabled: rgba(0x908190ff).into(), + text_accent: rgba(0x5169ebff).into(), + icon_muted: rgba(0xa899a8ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("preproc".into(), rgba(0xf7f3f7ff).into()), + ("number".into(), rgba(0xa65825ff).into()), + ("boolean".into(), rgba(0x918b3aff).into()), + ("embedded".into(), rgba(0xf7f3f7ff).into()), + ("variable.special".into(), rgba(0x7b58bfff).into()), + ("operator".into(), rgba(0xab9babff).into()), + ("punctuation.delimiter".into(), rgba(0xab9babff).into()), + ("primary".into(), rgba(0xd8cad8ff).into()), + ("punctuation.bracket".into(), rgba(0xab9babff).into()), + ("comment.doc".into(), rgba(0xab9babff).into()), + ("variant".into(), rgba(0xbb8a34ff).into()), + ("attribute".into(), rgba(0x5169ebff).into()), + ("property".into(), rgba(0xca3f2aff).into()), + ("keyword".into(), rgba(0x7b58bfff).into()), + ("hint".into(), rgba(0x8d70a8ff).into()), + ("string.special.symbol".into(), rgba(0x918b3aff).into()), + ("punctuation.special".into(), rgba(0xcc32ccff).into()), + ("link_uri".into(), rgba(0x918b3aff).into()), + ("link_text".into(), rgba(0xa65827ff).into()), + ("enum".into(), rgba(0xa65827ff).into()), + ("function".into(), rgba(0x506aecff).into()), + ( + "function.special.definition".into(), + rgba(0xbb8a34ff).into(), + ), + ("constant".into(), rgba(0x918b3aff).into()), + ("title".into(), rgba(0xf7f3f7ff).into()), + ("string.regex".into(), rgba(0x149393ff).into()), + ("variable".into(), rgba(0xd8cad8ff).into()), + ("comment".into(), rgba(0x776977ff).into()), + ("predictive".into(), rgba(0x75588fff).into()), + ("function.method".into(), rgba(0x506aecff).into()), + ("type".into(), rgba(0xbb8a34ff).into()), + ("punctuation".into(), rgba(0xd8cad8ff).into()), + ("emphasis".into(), rgba(0x5169ebff).into()), + ("emphasis.strong".into(), rgba(0x5169ebff).into()), + ("tag".into(), rgba(0x5169ebff).into()), + ("text.literal".into(), rgba(0xa65827ff).into()), + ("string".into(), rgba(0x918b3aff).into()), + ("string.escape".into(), rgba(0xab9babff).into()), + ("constructor".into(), rgba(0x5169ebff).into()), + ("label".into(), rgba(0x5169ebff).into()), + ("punctuation.list_marker".into(), rgba(0xd8cad8ff).into()), + ("string.special".into(), rgba(0xcc32ccff).into()), + ], + }, + status_bar: rgba(0x433a43ff).into(), + title_bar: rgba(0x433a43ff).into(), + toolbar: rgba(0x1b181bff).into(), + tab_bar: rgba(0x252025ff).into(), + editor: rgba(0x1b181bff).into(), + editor_subheader: rgba(0x252025ff).into(), + editor_active_line: rgba(0x252025ff).into(), + terminal: rgba(0x1b181bff).into(), + image_fallback_background: rgba(0x433a43ff).into(), + git_created: rgba(0x918b3aff).into(), + git_modified: rgba(0x5169ebff).into(), + git_deleted: rgba(0xca3f2bff).into(), + git_conflict: rgba(0xbb8a35ff).into(), + git_ignored: rgba(0x908190ff).into(), + git_renamed: rgba(0xbb8a35ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x5169ebff).into(), + selection: rgba(0x5169eb3d).into(), + }, + PlayerTheme { + cursor: rgba(0x918b3aff).into(), + selection: rgba(0x918b3a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xcc34ccff).into(), + selection: rgba(0xcc34cc3d).into(), + }, + PlayerTheme { + cursor: rgba(0xa65827ff).into(), + selection: rgba(0xa658273d).into(), + }, + PlayerTheme { + cursor: rgba(0x7b58bfff).into(), + selection: rgba(0x7b58bf3d).into(), + }, + PlayerTheme { + cursor: rgba(0x189393ff).into(), + selection: rgba(0x1893933d).into(), + }, + PlayerTheme { + cursor: rgba(0xca3f2bff).into(), + selection: rgba(0xca3f2b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xbb8a35ff).into(), + selection: rgba(0xbb8a353d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_heath_light.rs b/crates/theme2/src/themes/atelier_heath_light.rs new file mode 100644 index 0000000000..540f84febf --- /dev/null +++ b/crates/theme2/src/themes/atelier_heath_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_heath_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Heath Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xad9dadff).into(), + border_variant: rgba(0xad9dadff).into(), + border_focused: rgba(0xcac7faff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xc6b8c6ff).into(), + surface: rgba(0xe0d5e0ff).into(), + background: rgba(0xc6b8c6ff).into(), + filled_element: rgba(0xc6b8c6ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xe2dffcff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xe2dffcff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x1b181bff).into(), + text_muted: rgba(0x6b5e6bff).into(), + text_placeholder: rgba(0xca402bff).into(), + text_disabled: rgba(0x857785ff).into(), + text_accent: rgba(0x5169ebff).into(), + icon_muted: rgba(0x6b5e6bff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("enum".into(), rgba(0xa65927ff).into()), + ("string.escape".into(), rgba(0x695d69ff).into()), + ("link_uri".into(), rgba(0x918b3bff).into()), + ("function.method".into(), rgba(0x506aecff).into()), + ("comment.doc".into(), rgba(0x695d69ff).into()), + ("property".into(), rgba(0xca3f2aff).into()), + ("string.special".into(), rgba(0xcc32ccff).into()), + ("tag".into(), rgba(0x5169ebff).into()), + ("embedded".into(), rgba(0x1b181bff).into()), + ("primary".into(), rgba(0x292329ff).into()), + ("punctuation".into(), rgba(0x292329ff).into()), + ("punctuation.special".into(), rgba(0xcc32ccff).into()), + ("type".into(), rgba(0xbb8a34ff).into()), + ("number".into(), rgba(0xa65825ff).into()), + ("function".into(), rgba(0x506aecff).into()), + ("preproc".into(), rgba(0x1b181bff).into()), + ("punctuation.bracket".into(), rgba(0x695d69ff).into()), + ("punctuation.delimiter".into(), rgba(0x695d69ff).into()), + ("variable".into(), rgba(0x292329ff).into()), + ( + "function.special.definition".into(), + rgba(0xbb8a34ff).into(), + ), + ("label".into(), rgba(0x5169ebff).into()), + ("constructor".into(), rgba(0x5169ebff).into()), + ("emphasis.strong".into(), rgba(0x5169ebff).into()), + ("constant".into(), rgba(0x918b3bff).into()), + ("keyword".into(), rgba(0x7b58bfff).into()), + ("variable.special".into(), rgba(0x7b58bfff).into()), + ("variant".into(), rgba(0xbb8a34ff).into()), + ("title".into(), rgba(0x1b181bff).into()), + ("attribute".into(), rgba(0x5169ebff).into()), + ("comment".into(), rgba(0x9e8f9eff).into()), + ("string.special.symbol".into(), rgba(0x918b3aff).into()), + ("predictive".into(), rgba(0xa487bfff).into()), + ("link_text".into(), rgba(0xa65927ff).into()), + ("punctuation.list_marker".into(), rgba(0x292329ff).into()), + ("boolean".into(), rgba(0x918b3bff).into()), + ("text.literal".into(), rgba(0xa65927ff).into()), + ("emphasis".into(), rgba(0x5169ebff).into()), + ("string.regex".into(), rgba(0x149393ff).into()), + ("hint".into(), rgba(0x8c70a6ff).into()), + ("string".into(), rgba(0x918b3aff).into()), + ("operator".into(), rgba(0x695d69ff).into()), + ], + }, + status_bar: rgba(0xc6b8c6ff).into(), + title_bar: rgba(0xc6b8c6ff).into(), + toolbar: rgba(0xf7f3f7ff).into(), + tab_bar: rgba(0xe0d5e0ff).into(), + editor: rgba(0xf7f3f7ff).into(), + editor_subheader: rgba(0xe0d5e0ff).into(), + editor_active_line: rgba(0xe0d5e0ff).into(), + terminal: rgba(0xf7f3f7ff).into(), + image_fallback_background: rgba(0xc6b8c6ff).into(), + git_created: rgba(0x918b3bff).into(), + git_modified: rgba(0x5169ebff).into(), + git_deleted: rgba(0xca402bff).into(), + git_conflict: rgba(0xbb8a35ff).into(), + git_ignored: rgba(0x857785ff).into(), + git_renamed: rgba(0xbb8a35ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x5169ebff).into(), + selection: rgba(0x5169eb3d).into(), + }, + PlayerTheme { + cursor: rgba(0x918b3bff).into(), + selection: rgba(0x918b3b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xcc34ccff).into(), + selection: rgba(0xcc34cc3d).into(), + }, + PlayerTheme { + cursor: rgba(0xa65927ff).into(), + selection: rgba(0xa659273d).into(), + }, + PlayerTheme { + cursor: rgba(0x7a5ac0ff).into(), + selection: rgba(0x7a5ac03d).into(), + }, + PlayerTheme { + cursor: rgba(0x189393ff).into(), + selection: rgba(0x1893933d).into(), + }, + PlayerTheme { + cursor: rgba(0xca402bff).into(), + selection: rgba(0xca402b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xbb8a35ff).into(), + selection: rgba(0xbb8a353d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_lakeside_dark.rs b/crates/theme2/src/themes/atelier_lakeside_dark.rs new file mode 100644 index 0000000000..015a9d0751 --- /dev/null +++ b/crates/theme2/src/themes/atelier_lakeside_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_lakeside_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Lakeside Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x4f6a78ff).into(), + border_variant: rgba(0x4f6a78ff).into(), + border_focused: rgba(0x1a2f3cff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x33444dff).into(), + surface: rgba(0x1c2529ff).into(), + background: rgba(0x33444dff).into(), + filled_element: rgba(0x33444dff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x121c24ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x121c24ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xebf8ffff).into(), + text_muted: rgba(0x7c9fb3ff).into(), + text_placeholder: rgba(0xd22e72ff).into(), + text_disabled: rgba(0x688c9dff).into(), + text_accent: rgba(0x267eadff).into(), + icon_muted: rgba(0x7c9fb3ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("punctuation.bracket".into(), rgba(0x7ea2b4ff).into()), + ("punctuation.special".into(), rgba(0xb72cd2ff).into()), + ("property".into(), rgba(0xd22c72ff).into()), + ("function.method".into(), rgba(0x247eadff).into()), + ("comment".into(), rgba(0x5a7b8cff).into()), + ("constructor".into(), rgba(0x267eadff).into()), + ("boolean".into(), rgba(0x558c3aff).into()), + ("hint".into(), rgba(0x52809aff).into()), + ("label".into(), rgba(0x267eadff).into()), + ("string.special".into(), rgba(0xb72cd2ff).into()), + ("title".into(), rgba(0xebf8ffff).into()), + ("punctuation.list_marker".into(), rgba(0xc1e4f6ff).into()), + ("emphasis.strong".into(), rgba(0x267eadff).into()), + ("enum".into(), rgba(0x935b25ff).into()), + ("type".into(), rgba(0x8a8a0eff).into()), + ("tag".into(), rgba(0x267eadff).into()), + ("punctuation.delimiter".into(), rgba(0x7ea2b4ff).into()), + ("primary".into(), rgba(0xc1e4f6ff).into()), + ("link_text".into(), rgba(0x935b25ff).into()), + ("variable".into(), rgba(0xc1e4f6ff).into()), + ("variable.special".into(), rgba(0x6a6ab7ff).into()), + ("string.special.symbol".into(), rgba(0x558c3aff).into()), + ("link_uri".into(), rgba(0x558c3aff).into()), + ("function".into(), rgba(0x247eadff).into()), + ("predictive".into(), rgba(0x426f88ff).into()), + ("punctuation".into(), rgba(0xc1e4f6ff).into()), + ("string.escape".into(), rgba(0x7ea2b4ff).into()), + ("keyword".into(), rgba(0x6a6ab7ff).into()), + ("attribute".into(), rgba(0x267eadff).into()), + ("string.regex".into(), rgba(0x2c8f6eff).into()), + ("embedded".into(), rgba(0xebf8ffff).into()), + ("emphasis".into(), rgba(0x267eadff).into()), + ("string".into(), rgba(0x558c3aff).into()), + ("operator".into(), rgba(0x7ea2b4ff).into()), + ("text.literal".into(), rgba(0x935b25ff).into()), + ("constant".into(), rgba(0x558c3aff).into()), + ("comment.doc".into(), rgba(0x7ea2b4ff).into()), + ("number".into(), rgba(0x935c24ff).into()), + ("preproc".into(), rgba(0xebf8ffff).into()), + ( + "function.special.definition".into(), + rgba(0x8a8a0eff).into(), + ), + ("variant".into(), rgba(0x8a8a0eff).into()), + ], + }, + status_bar: rgba(0x33444dff).into(), + title_bar: rgba(0x33444dff).into(), + toolbar: rgba(0x161b1dff).into(), + tab_bar: rgba(0x1c2529ff).into(), + editor: rgba(0x161b1dff).into(), + editor_subheader: rgba(0x1c2529ff).into(), + editor_active_line: rgba(0x1c2529ff).into(), + terminal: rgba(0x161b1dff).into(), + image_fallback_background: rgba(0x33444dff).into(), + git_created: rgba(0x558c3aff).into(), + git_modified: rgba(0x267eadff).into(), + git_deleted: rgba(0xd22e72ff).into(), + git_conflict: rgba(0x8a8a10ff).into(), + git_ignored: rgba(0x688c9dff).into(), + git_renamed: rgba(0x8a8a10ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x267eadff).into(), + selection: rgba(0x267ead3d).into(), + }, + PlayerTheme { + cursor: rgba(0x558c3aff).into(), + selection: rgba(0x558c3a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xb72ed2ff).into(), + selection: rgba(0xb72ed23d).into(), + }, + PlayerTheme { + cursor: rgba(0x935b25ff).into(), + selection: rgba(0x935b253d).into(), + }, + PlayerTheme { + cursor: rgba(0x6a6ab7ff).into(), + selection: rgba(0x6a6ab73d).into(), + }, + PlayerTheme { + cursor: rgba(0x2d8f6fff).into(), + selection: rgba(0x2d8f6f3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd22e72ff).into(), + selection: rgba(0xd22e723d).into(), + }, + PlayerTheme { + cursor: rgba(0x8a8a10ff).into(), + selection: rgba(0x8a8a103d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_lakeside_light.rs b/crates/theme2/src/themes/atelier_lakeside_light.rs new file mode 100644 index 0000000000..85d5e3d782 --- /dev/null +++ b/crates/theme2/src/themes/atelier_lakeside_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_lakeside_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Lakeside Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x80a4b6ff).into(), + border_variant: rgba(0x80a4b6ff).into(), + border_focused: rgba(0xb9cee0ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xa6cadcff).into(), + surface: rgba(0xcdeaf9ff).into(), + background: rgba(0xa6cadcff).into(), + filled_element: rgba(0xa6cadcff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xd8e4eeff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xd8e4eeff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x161b1dff).into(), + text_muted: rgba(0x526f7dff).into(), + text_placeholder: rgba(0xd22e71ff).into(), + text_disabled: rgba(0x628496ff).into(), + text_accent: rgba(0x267eadff).into(), + icon_muted: rgba(0x526f7dff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("emphasis".into(), rgba(0x267eadff).into()), + ("number".into(), rgba(0x935c24ff).into()), + ("embedded".into(), rgba(0x161b1dff).into()), + ("link_text".into(), rgba(0x935c25ff).into()), + ("string".into(), rgba(0x558c3aff).into()), + ("constructor".into(), rgba(0x267eadff).into()), + ("punctuation.list_marker".into(), rgba(0x1f292eff).into()), + ("string.special".into(), rgba(0xb72cd2ff).into()), + ("title".into(), rgba(0x161b1dff).into()), + ("variant".into(), rgba(0x8a8a0eff).into()), + ("tag".into(), rgba(0x267eadff).into()), + ("attribute".into(), rgba(0x267eadff).into()), + ("keyword".into(), rgba(0x6a6ab7ff).into()), + ("enum".into(), rgba(0x935c25ff).into()), + ("function".into(), rgba(0x247eadff).into()), + ("string.escape".into(), rgba(0x516d7bff).into()), + ("operator".into(), rgba(0x516d7bff).into()), + ("function.method".into(), rgba(0x247eadff).into()), + ( + "function.special.definition".into(), + rgba(0x8a8a0eff).into(), + ), + ("punctuation.delimiter".into(), rgba(0x516d7bff).into()), + ("comment".into(), rgba(0x7094a7ff).into()), + ("primary".into(), rgba(0x1f292eff).into()), + ("punctuation.bracket".into(), rgba(0x516d7bff).into()), + ("variable".into(), rgba(0x1f292eff).into()), + ("emphasis.strong".into(), rgba(0x267eadff).into()), + ("predictive".into(), rgba(0x6a97b2ff).into()), + ("punctuation.special".into(), rgba(0xb72cd2ff).into()), + ("hint".into(), rgba(0x5a87a0ff).into()), + ("text.literal".into(), rgba(0x935c25ff).into()), + ("string.special.symbol".into(), rgba(0x558c3aff).into()), + ("comment.doc".into(), rgba(0x516d7bff).into()), + ("constant".into(), rgba(0x568c3bff).into()), + ("boolean".into(), rgba(0x568c3bff).into()), + ("preproc".into(), rgba(0x161b1dff).into()), + ("variable.special".into(), rgba(0x6a6ab7ff).into()), + ("link_uri".into(), rgba(0x568c3bff).into()), + ("string.regex".into(), rgba(0x2c8f6eff).into()), + ("punctuation".into(), rgba(0x1f292eff).into()), + ("property".into(), rgba(0xd22c72ff).into()), + ("label".into(), rgba(0x267eadff).into()), + ("type".into(), rgba(0x8a8a0eff).into()), + ], + }, + status_bar: rgba(0xa6cadcff).into(), + title_bar: rgba(0xa6cadcff).into(), + toolbar: rgba(0xebf8ffff).into(), + tab_bar: rgba(0xcdeaf9ff).into(), + editor: rgba(0xebf8ffff).into(), + editor_subheader: rgba(0xcdeaf9ff).into(), + editor_active_line: rgba(0xcdeaf9ff).into(), + terminal: rgba(0xebf8ffff).into(), + image_fallback_background: rgba(0xa6cadcff).into(), + git_created: rgba(0x568c3bff).into(), + git_modified: rgba(0x267eadff).into(), + git_deleted: rgba(0xd22e71ff).into(), + git_conflict: rgba(0x8a8a10ff).into(), + git_ignored: rgba(0x628496ff).into(), + git_renamed: rgba(0x8a8a10ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x267eadff).into(), + selection: rgba(0x267ead3d).into(), + }, + PlayerTheme { + cursor: rgba(0x568c3bff).into(), + selection: rgba(0x568c3b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xb72ed2ff).into(), + selection: rgba(0xb72ed23d).into(), + }, + PlayerTheme { + cursor: rgba(0x935c25ff).into(), + selection: rgba(0x935c253d).into(), + }, + PlayerTheme { + cursor: rgba(0x6c6ab7ff).into(), + selection: rgba(0x6c6ab73d).into(), + }, + PlayerTheme { + cursor: rgba(0x2e8f6eff).into(), + selection: rgba(0x2e8f6e3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd22e71ff).into(), + selection: rgba(0xd22e713d).into(), + }, + PlayerTheme { + cursor: rgba(0x8a8a10ff).into(), + selection: rgba(0x8a8a103d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_plateau_dark.rs b/crates/theme2/src/themes/atelier_plateau_dark.rs new file mode 100644 index 0000000000..231572ad65 --- /dev/null +++ b/crates/theme2/src/themes/atelier_plateau_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_plateau_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Plateau Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x564e4eff).into(), + border_variant: rgba(0x564e4eff).into(), + border_focused: rgba(0x2c2b45ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x3b3535ff).into(), + surface: rgba(0x252020ff).into(), + background: rgba(0x3b3535ff).into(), + filled_element: rgba(0x3b3535ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x1c1b29ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x1c1b29ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf4ececff).into(), + text_muted: rgba(0x898383ff).into(), + text_placeholder: rgba(0xca4848ff).into(), + text_disabled: rgba(0x756e6eff).into(), + text_accent: rgba(0x7272caff).into(), + icon_muted: rgba(0x898383ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("variant".into(), rgba(0xa06d3aff).into()), + ("label".into(), rgba(0x7272caff).into()), + ("punctuation.delimiter".into(), rgba(0x8a8585ff).into()), + ("string.regex".into(), rgba(0x5485b6ff).into()), + ("variable.special".into(), rgba(0x8464c4ff).into()), + ("string".into(), rgba(0x4b8b8bff).into()), + ("property".into(), rgba(0xca4848ff).into()), + ("hint".into(), rgba(0x8a647aff).into()), + ("comment.doc".into(), rgba(0x8a8585ff).into()), + ("attribute".into(), rgba(0x7272caff).into()), + ("tag".into(), rgba(0x7272caff).into()), + ("constructor".into(), rgba(0x7272caff).into()), + ("boolean".into(), rgba(0x4b8b8bff).into()), + ("preproc".into(), rgba(0xf4ececff).into()), + ("constant".into(), rgba(0x4b8b8bff).into()), + ("punctuation.special".into(), rgba(0xbd5187ff).into()), + ("function.method".into(), rgba(0x7272caff).into()), + ("comment".into(), rgba(0x655d5dff).into()), + ("variable".into(), rgba(0xe7dfdfff).into()), + ("primary".into(), rgba(0xe7dfdfff).into()), + ("title".into(), rgba(0xf4ececff).into()), + ("emphasis".into(), rgba(0x7272caff).into()), + ("emphasis.strong".into(), rgba(0x7272caff).into()), + ("function".into(), rgba(0x7272caff).into()), + ("type".into(), rgba(0xa06d3aff).into()), + ("operator".into(), rgba(0x8a8585ff).into()), + ("embedded".into(), rgba(0xf4ececff).into()), + ("predictive".into(), rgba(0x795369ff).into()), + ("punctuation".into(), rgba(0xe7dfdfff).into()), + ("link_text".into(), rgba(0xb4593bff).into()), + ("enum".into(), rgba(0xb4593bff).into()), + ("string.special".into(), rgba(0xbd5187ff).into()), + ("text.literal".into(), rgba(0xb4593bff).into()), + ("string.escape".into(), rgba(0x8a8585ff).into()), + ( + "function.special.definition".into(), + rgba(0xa06d3aff).into(), + ), + ("keyword".into(), rgba(0x8464c4ff).into()), + ("link_uri".into(), rgba(0x4b8b8bff).into()), + ("number".into(), rgba(0xb4593bff).into()), + ("punctuation.bracket".into(), rgba(0x8a8585ff).into()), + ("string.special.symbol".into(), rgba(0x4b8b8bff).into()), + ("punctuation.list_marker".into(), rgba(0xe7dfdfff).into()), + ], + }, + status_bar: rgba(0x3b3535ff).into(), + title_bar: rgba(0x3b3535ff).into(), + toolbar: rgba(0x1b1818ff).into(), + tab_bar: rgba(0x252020ff).into(), + editor: rgba(0x1b1818ff).into(), + editor_subheader: rgba(0x252020ff).into(), + editor_active_line: rgba(0x252020ff).into(), + terminal: rgba(0x1b1818ff).into(), + image_fallback_background: rgba(0x3b3535ff).into(), + git_created: rgba(0x4b8b8bff).into(), + git_modified: rgba(0x7272caff).into(), + git_deleted: rgba(0xca4848ff).into(), + git_conflict: rgba(0xa06d3aff).into(), + git_ignored: rgba(0x756e6eff).into(), + git_renamed: rgba(0xa06d3aff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x7272caff).into(), + selection: rgba(0x7272ca3d).into(), + }, + PlayerTheme { + cursor: rgba(0x4b8b8bff).into(), + selection: rgba(0x4b8b8b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xbd5187ff).into(), + selection: rgba(0xbd51873d).into(), + }, + PlayerTheme { + cursor: rgba(0xb4593bff).into(), + selection: rgba(0xb4593b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x8464c4ff).into(), + selection: rgba(0x8464c43d).into(), + }, + PlayerTheme { + cursor: rgba(0x5485b6ff).into(), + selection: rgba(0x5485b63d).into(), + }, + PlayerTheme { + cursor: rgba(0xca4848ff).into(), + selection: rgba(0xca48483d).into(), + }, + PlayerTheme { + cursor: rgba(0xa06d3aff).into(), + selection: rgba(0xa06d3a3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_plateau_light.rs b/crates/theme2/src/themes/atelier_plateau_light.rs new file mode 100644 index 0000000000..efca15d7d6 --- /dev/null +++ b/crates/theme2/src/themes/atelier_plateau_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_plateau_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Plateau Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x8e8989ff).into(), + border_variant: rgba(0x8e8989ff).into(), + border_focused: rgba(0xcecaecff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xc1bbbbff).into(), + surface: rgba(0xebe3e3ff).into(), + background: rgba(0xc1bbbbff).into(), + filled_element: rgba(0xc1bbbbff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xe4e1f5ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xe4e1f5ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x1b1818ff).into(), + text_muted: rgba(0x5a5252ff).into(), + text_placeholder: rgba(0xca4a4aff).into(), + text_disabled: rgba(0x6e6666ff).into(), + text_accent: rgba(0x7272caff).into(), + icon_muted: rgba(0x5a5252ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("text.literal".into(), rgba(0xb45a3cff).into()), + ("punctuation.special".into(), rgba(0xbd5187ff).into()), + ("variant".into(), rgba(0xa06d3aff).into()), + ("punctuation".into(), rgba(0x292424ff).into()), + ("string.escape".into(), rgba(0x585050ff).into()), + ("emphasis".into(), rgba(0x7272caff).into()), + ("title".into(), rgba(0x1b1818ff).into()), + ("constructor".into(), rgba(0x7272caff).into()), + ("variable".into(), rgba(0x292424ff).into()), + ("predictive".into(), rgba(0xa27a91ff).into()), + ("label".into(), rgba(0x7272caff).into()), + ("function.method".into(), rgba(0x7272caff).into()), + ("link_uri".into(), rgba(0x4c8b8bff).into()), + ("punctuation.delimiter".into(), rgba(0x585050ff).into()), + ("link_text".into(), rgba(0xb45a3cff).into()), + ("hint".into(), rgba(0x91697fff).into()), + ("emphasis.strong".into(), rgba(0x7272caff).into()), + ("attribute".into(), rgba(0x7272caff).into()), + ("boolean".into(), rgba(0x4c8b8bff).into()), + ("string.special.symbol".into(), rgba(0x4b8b8bff).into()), + ("string".into(), rgba(0x4b8b8bff).into()), + ("type".into(), rgba(0xa06d3aff).into()), + ("string.regex".into(), rgba(0x5485b6ff).into()), + ("comment.doc".into(), rgba(0x585050ff).into()), + ("string.special".into(), rgba(0xbd5187ff).into()), + ("property".into(), rgba(0xca4848ff).into()), + ("preproc".into(), rgba(0x1b1818ff).into()), + ("embedded".into(), rgba(0x1b1818ff).into()), + ("comment".into(), rgba(0x7e7777ff).into()), + ("primary".into(), rgba(0x292424ff).into()), + ("number".into(), rgba(0xb4593bff).into()), + ("function".into(), rgba(0x7272caff).into()), + ("punctuation.bracket".into(), rgba(0x585050ff).into()), + ("tag".into(), rgba(0x7272caff).into()), + ("punctuation.list_marker".into(), rgba(0x292424ff).into()), + ( + "function.special.definition".into(), + rgba(0xa06d3aff).into(), + ), + ("enum".into(), rgba(0xb45a3cff).into()), + ("keyword".into(), rgba(0x8464c4ff).into()), + ("operator".into(), rgba(0x585050ff).into()), + ("variable.special".into(), rgba(0x8464c4ff).into()), + ("constant".into(), rgba(0x4c8b8bff).into()), + ], + }, + status_bar: rgba(0xc1bbbbff).into(), + title_bar: rgba(0xc1bbbbff).into(), + toolbar: rgba(0xf4ececff).into(), + tab_bar: rgba(0xebe3e3ff).into(), + editor: rgba(0xf4ececff).into(), + editor_subheader: rgba(0xebe3e3ff).into(), + editor_active_line: rgba(0xebe3e3ff).into(), + terminal: rgba(0xf4ececff).into(), + image_fallback_background: rgba(0xc1bbbbff).into(), + git_created: rgba(0x4c8b8bff).into(), + git_modified: rgba(0x7272caff).into(), + git_deleted: rgba(0xca4a4aff).into(), + git_conflict: rgba(0xa06e3bff).into(), + git_ignored: rgba(0x6e6666ff).into(), + git_renamed: rgba(0xa06e3bff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x7272caff).into(), + selection: rgba(0x7272ca3d).into(), + }, + PlayerTheme { + cursor: rgba(0x4c8b8bff).into(), + selection: rgba(0x4c8b8b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xbd5186ff).into(), + selection: rgba(0xbd51863d).into(), + }, + PlayerTheme { + cursor: rgba(0xb45a3cff).into(), + selection: rgba(0xb45a3c3d).into(), + }, + PlayerTheme { + cursor: rgba(0x8464c4ff).into(), + selection: rgba(0x8464c43d).into(), + }, + PlayerTheme { + cursor: rgba(0x5485b5ff).into(), + selection: rgba(0x5485b53d).into(), + }, + PlayerTheme { + cursor: rgba(0xca4a4aff).into(), + selection: rgba(0xca4a4a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xa06e3bff).into(), + selection: rgba(0xa06e3b3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_savanna_dark.rs b/crates/theme2/src/themes/atelier_savanna_dark.rs new file mode 100644 index 0000000000..7314824f18 --- /dev/null +++ b/crates/theme2/src/themes/atelier_savanna_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_savanna_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Savanna Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x505e55ff).into(), + border_variant: rgba(0x505e55ff).into(), + border_focused: rgba(0x1f3233ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x353f39ff).into(), + surface: rgba(0x1f2621ff).into(), + background: rgba(0x353f39ff).into(), + filled_element: rgba(0x353f39ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x151e20ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x151e20ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xecf4eeff).into(), + text_muted: rgba(0x859188ff).into(), + text_placeholder: rgba(0xb16038ff).into(), + text_disabled: rgba(0x6f7e74ff).into(), + text_accent: rgba(0x468b8fff).into(), + icon_muted: rgba(0x859188ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("function.method".into(), rgba(0x468b8fff).into()), + ("title".into(), rgba(0xecf4eeff).into()), + ("label".into(), rgba(0x468b8fff).into()), + ("text.literal".into(), rgba(0x9f703bff).into()), + ("boolean".into(), rgba(0x479962ff).into()), + ("punctuation.list_marker".into(), rgba(0xdfe7e2ff).into()), + ("string.escape".into(), rgba(0x87928aff).into()), + ("string.special".into(), rgba(0x857368ff).into()), + ("punctuation.delimiter".into(), rgba(0x87928aff).into()), + ("tag".into(), rgba(0x468b8fff).into()), + ("property".into(), rgba(0xb16038ff).into()), + ("preproc".into(), rgba(0xecf4eeff).into()), + ("primary".into(), rgba(0xdfe7e2ff).into()), + ("link_uri".into(), rgba(0x479962ff).into()), + ("comment".into(), rgba(0x5f6d64ff).into()), + ("type".into(), rgba(0xa07d3aff).into()), + ("hint".into(), rgba(0x607e76ff).into()), + ("punctuation".into(), rgba(0xdfe7e2ff).into()), + ("string.special.symbol".into(), rgba(0x479962ff).into()), + ("emphasis.strong".into(), rgba(0x468b8fff).into()), + ("keyword".into(), rgba(0x55859bff).into()), + ("comment.doc".into(), rgba(0x87928aff).into()), + ("punctuation.bracket".into(), rgba(0x87928aff).into()), + ("constant".into(), rgba(0x479962ff).into()), + ("link_text".into(), rgba(0x9f703bff).into()), + ("number".into(), rgba(0x9f703bff).into()), + ("function".into(), rgba(0x468b8fff).into()), + ("variable".into(), rgba(0xdfe7e2ff).into()), + ("emphasis".into(), rgba(0x468b8fff).into()), + ("punctuation.special".into(), rgba(0x857368ff).into()), + ("constructor".into(), rgba(0x468b8fff).into()), + ("variable.special".into(), rgba(0x55859bff).into()), + ("operator".into(), rgba(0x87928aff).into()), + ("enum".into(), rgba(0x9f703bff).into()), + ("string.regex".into(), rgba(0x1b9aa0ff).into()), + ("attribute".into(), rgba(0x468b8fff).into()), + ("predictive".into(), rgba(0x506d66ff).into()), + ("string".into(), rgba(0x479962ff).into()), + ("embedded".into(), rgba(0xecf4eeff).into()), + ("variant".into(), rgba(0xa07d3aff).into()), + ( + "function.special.definition".into(), + rgba(0xa07d3aff).into(), + ), + ], + }, + status_bar: rgba(0x353f39ff).into(), + title_bar: rgba(0x353f39ff).into(), + toolbar: rgba(0x171c19ff).into(), + tab_bar: rgba(0x1f2621ff).into(), + editor: rgba(0x171c19ff).into(), + editor_subheader: rgba(0x1f2621ff).into(), + editor_active_line: rgba(0x1f2621ff).into(), + terminal: rgba(0x171c19ff).into(), + image_fallback_background: rgba(0x353f39ff).into(), + git_created: rgba(0x479962ff).into(), + git_modified: rgba(0x468b8fff).into(), + git_deleted: rgba(0xb16038ff).into(), + git_conflict: rgba(0xa07d3aff).into(), + git_ignored: rgba(0x6f7e74ff).into(), + git_renamed: rgba(0xa07d3aff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x468b8fff).into(), + selection: rgba(0x468b8f3d).into(), + }, + PlayerTheme { + cursor: rgba(0x479962ff).into(), + selection: rgba(0x4799623d).into(), + }, + PlayerTheme { + cursor: rgba(0x857368ff).into(), + selection: rgba(0x8573683d).into(), + }, + PlayerTheme { + cursor: rgba(0x9f703bff).into(), + selection: rgba(0x9f703b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x55859bff).into(), + selection: rgba(0x55859b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x1d9aa0ff).into(), + selection: rgba(0x1d9aa03d).into(), + }, + PlayerTheme { + cursor: rgba(0xb16038ff).into(), + selection: rgba(0xb160383d).into(), + }, + PlayerTheme { + cursor: rgba(0xa07d3aff).into(), + selection: rgba(0xa07d3a3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_savanna_light.rs b/crates/theme2/src/themes/atelier_savanna_light.rs new file mode 100644 index 0000000000..32df2e1691 --- /dev/null +++ b/crates/theme2/src/themes/atelier_savanna_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_savanna_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Savanna Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x8b968eff).into(), + border_variant: rgba(0x8b968eff).into(), + border_focused: rgba(0xbed4d6ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xbcc5bfff).into(), + surface: rgba(0xe3ebe6ff).into(), + background: rgba(0xbcc5bfff).into(), + filled_element: rgba(0xbcc5bfff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xdae7e8ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xdae7e8ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x171c19ff).into(), + text_muted: rgba(0x546259ff).into(), + text_placeholder: rgba(0xb16139ff).into(), + text_disabled: rgba(0x68766dff).into(), + text_accent: rgba(0x488b90ff).into(), + icon_muted: rgba(0x546259ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("text.literal".into(), rgba(0x9f713cff).into()), + ("string".into(), rgba(0x479962ff).into()), + ("punctuation.special".into(), rgba(0x857368ff).into()), + ("type".into(), rgba(0xa07d3aff).into()), + ("enum".into(), rgba(0x9f713cff).into()), + ("title".into(), rgba(0x171c19ff).into()), + ("comment".into(), rgba(0x77877cff).into()), + ("predictive".into(), rgba(0x75958bff).into()), + ("punctuation.list_marker".into(), rgba(0x232a25ff).into()), + ("string.special.symbol".into(), rgba(0x479962ff).into()), + ("constructor".into(), rgba(0x488b90ff).into()), + ("variable".into(), rgba(0x232a25ff).into()), + ("label".into(), rgba(0x488b90ff).into()), + ("attribute".into(), rgba(0x488b90ff).into()), + ("constant".into(), rgba(0x499963ff).into()), + ("function".into(), rgba(0x468b8fff).into()), + ("variable.special".into(), rgba(0x55859bff).into()), + ("keyword".into(), rgba(0x55859bff).into()), + ("number".into(), rgba(0x9f703bff).into()), + ("boolean".into(), rgba(0x499963ff).into()), + ("embedded".into(), rgba(0x171c19ff).into()), + ("string.special".into(), rgba(0x857368ff).into()), + ("emphasis.strong".into(), rgba(0x488b90ff).into()), + ("string.regex".into(), rgba(0x1b9aa0ff).into()), + ("hint".into(), rgba(0x66847cff).into()), + ("preproc".into(), rgba(0x171c19ff).into()), + ("link_uri".into(), rgba(0x499963ff).into()), + ("variant".into(), rgba(0xa07d3aff).into()), + ("function.method".into(), rgba(0x468b8fff).into()), + ("punctuation.bracket".into(), rgba(0x526057ff).into()), + ("punctuation.delimiter".into(), rgba(0x526057ff).into()), + ("punctuation".into(), rgba(0x232a25ff).into()), + ("primary".into(), rgba(0x232a25ff).into()), + ("string.escape".into(), rgba(0x526057ff).into()), + ("property".into(), rgba(0xb16038ff).into()), + ("operator".into(), rgba(0x526057ff).into()), + ("comment.doc".into(), rgba(0x526057ff).into()), + ( + "function.special.definition".into(), + rgba(0xa07d3aff).into(), + ), + ("link_text".into(), rgba(0x9f713cff).into()), + ("tag".into(), rgba(0x488b90ff).into()), + ("emphasis".into(), rgba(0x488b90ff).into()), + ], + }, + status_bar: rgba(0xbcc5bfff).into(), + title_bar: rgba(0xbcc5bfff).into(), + toolbar: rgba(0xecf4eeff).into(), + tab_bar: rgba(0xe3ebe6ff).into(), + editor: rgba(0xecf4eeff).into(), + editor_subheader: rgba(0xe3ebe6ff).into(), + editor_active_line: rgba(0xe3ebe6ff).into(), + terminal: rgba(0xecf4eeff).into(), + image_fallback_background: rgba(0xbcc5bfff).into(), + git_created: rgba(0x499963ff).into(), + git_modified: rgba(0x488b90ff).into(), + git_deleted: rgba(0xb16139ff).into(), + git_conflict: rgba(0xa07d3bff).into(), + git_ignored: rgba(0x68766dff).into(), + git_renamed: rgba(0xa07d3bff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x488b90ff).into(), + selection: rgba(0x488b903d).into(), + }, + PlayerTheme { + cursor: rgba(0x499963ff).into(), + selection: rgba(0x4999633d).into(), + }, + PlayerTheme { + cursor: rgba(0x857368ff).into(), + selection: rgba(0x8573683d).into(), + }, + PlayerTheme { + cursor: rgba(0x9f713cff).into(), + selection: rgba(0x9f713c3d).into(), + }, + PlayerTheme { + cursor: rgba(0x55859bff).into(), + selection: rgba(0x55859b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x1e9aa0ff).into(), + selection: rgba(0x1e9aa03d).into(), + }, + PlayerTheme { + cursor: rgba(0xb16139ff).into(), + selection: rgba(0xb161393d).into(), + }, + PlayerTheme { + cursor: rgba(0xa07d3bff).into(), + selection: rgba(0xa07d3b3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_seaside_dark.rs b/crates/theme2/src/themes/atelier_seaside_dark.rs new file mode 100644 index 0000000000..6597e31de6 --- /dev/null +++ b/crates/theme2/src/themes/atelier_seaside_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_seaside_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Seaside Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x5c6c5cff).into(), + border_variant: rgba(0x5c6c5cff).into(), + border_focused: rgba(0x102667ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x3b453bff).into(), + surface: rgba(0x1f231fff).into(), + background: rgba(0x3b453bff).into(), + filled_element: rgba(0x3b453bff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x051949ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x051949ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf3faf3ff).into(), + text_muted: rgba(0x8ba48bff).into(), + text_placeholder: rgba(0xe61c3bff).into(), + text_disabled: rgba(0x778f77ff).into(), + text_accent: rgba(0x3e62f4ff).into(), + icon_muted: rgba(0x8ba48bff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("comment".into(), rgba(0x687d68ff).into()), + ("predictive".into(), rgba(0x00788bff).into()), + ("string.special".into(), rgba(0xe618c3ff).into()), + ("string.regex".into(), rgba(0x1899b3ff).into()), + ("boolean".into(), rgba(0x2aa329ff).into()), + ("string".into(), rgba(0x28a328ff).into()), + ("operator".into(), rgba(0x8ca68cff).into()), + ("primary".into(), rgba(0xcfe8cfff).into()), + ("number".into(), rgba(0x87711cff).into()), + ("punctuation.special".into(), rgba(0xe618c3ff).into()), + ("link_text".into(), rgba(0x87711dff).into()), + ("title".into(), rgba(0xf3faf3ff).into()), + ("comment.doc".into(), rgba(0x8ca68cff).into()), + ("label".into(), rgba(0x3e62f4ff).into()), + ("preproc".into(), rgba(0xf3faf3ff).into()), + ("punctuation.bracket".into(), rgba(0x8ca68cff).into()), + ("punctuation.delimiter".into(), rgba(0x8ca68cff).into()), + ("function.method".into(), rgba(0x3d62f5ff).into()), + ("tag".into(), rgba(0x3e62f4ff).into()), + ("embedded".into(), rgba(0xf3faf3ff).into()), + ("text.literal".into(), rgba(0x87711dff).into()), + ("punctuation".into(), rgba(0xcfe8cfff).into()), + ("string.special.symbol".into(), rgba(0x28a328ff).into()), + ("link_uri".into(), rgba(0x2aa329ff).into()), + ("keyword".into(), rgba(0xac2aeeff).into()), + ("function".into(), rgba(0x3d62f5ff).into()), + ("string.escape".into(), rgba(0x8ca68cff).into()), + ("variant".into(), rgba(0x98981bff).into()), + ( + "function.special.definition".into(), + rgba(0x98981bff).into(), + ), + ("constructor".into(), rgba(0x3e62f4ff).into()), + ("constant".into(), rgba(0x2aa329ff).into()), + ("hint".into(), rgba(0x008b9fff).into()), + ("type".into(), rgba(0x98981bff).into()), + ("emphasis".into(), rgba(0x3e62f4ff).into()), + ("variable".into(), rgba(0xcfe8cfff).into()), + ("emphasis.strong".into(), rgba(0x3e62f4ff).into()), + ("attribute".into(), rgba(0x3e62f4ff).into()), + ("enum".into(), rgba(0x87711dff).into()), + ("property".into(), rgba(0xe6183bff).into()), + ("punctuation.list_marker".into(), rgba(0xcfe8cfff).into()), + ("variable.special".into(), rgba(0xac2aeeff).into()), + ], + }, + status_bar: rgba(0x3b453bff).into(), + title_bar: rgba(0x3b453bff).into(), + toolbar: rgba(0x131513ff).into(), + tab_bar: rgba(0x1f231fff).into(), + editor: rgba(0x131513ff).into(), + editor_subheader: rgba(0x1f231fff).into(), + editor_active_line: rgba(0x1f231fff).into(), + terminal: rgba(0x131513ff).into(), + image_fallback_background: rgba(0x3b453bff).into(), + git_created: rgba(0x2aa329ff).into(), + git_modified: rgba(0x3e62f4ff).into(), + git_deleted: rgba(0xe61c3bff).into(), + git_conflict: rgba(0x98981bff).into(), + git_ignored: rgba(0x778f77ff).into(), + git_renamed: rgba(0x98981bff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x3e62f4ff).into(), + selection: rgba(0x3e62f43d).into(), + }, + PlayerTheme { + cursor: rgba(0x2aa329ff).into(), + selection: rgba(0x2aa3293d).into(), + }, + PlayerTheme { + cursor: rgba(0xe61cc3ff).into(), + selection: rgba(0xe61cc33d).into(), + }, + PlayerTheme { + cursor: rgba(0x87711dff).into(), + selection: rgba(0x87711d3d).into(), + }, + PlayerTheme { + cursor: rgba(0xac2dedff).into(), + selection: rgba(0xac2ded3d).into(), + }, + PlayerTheme { + cursor: rgba(0x1b99b3ff).into(), + selection: rgba(0x1b99b33d).into(), + }, + PlayerTheme { + cursor: rgba(0xe61c3bff).into(), + selection: rgba(0xe61c3b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x98981bff).into(), + selection: rgba(0x98981b3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_seaside_light.rs b/crates/theme2/src/themes/atelier_seaside_light.rs new file mode 100644 index 0000000000..a515bb4a71 --- /dev/null +++ b/crates/theme2/src/themes/atelier_seaside_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_seaside_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Seaside Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x8ea88eff).into(), + border_variant: rgba(0x8ea88eff).into(), + border_focused: rgba(0xc9c4fdff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xb4ceb4ff).into(), + surface: rgba(0xdaeedaff).into(), + background: rgba(0xb4ceb4ff).into(), + filled_element: rgba(0xb4ceb4ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xe1ddfeff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xe1ddfeff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x131513ff).into(), + text_muted: rgba(0x5f705fff).into(), + text_placeholder: rgba(0xe61c3dff).into(), + text_disabled: rgba(0x718771ff).into(), + text_accent: rgba(0x3e61f4ff).into(), + icon_muted: rgba(0x5f705fff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.escape".into(), rgba(0x5e6e5eff).into()), + ("boolean".into(), rgba(0x2aa32aff).into()), + ("string.special".into(), rgba(0xe618c3ff).into()), + ("comment".into(), rgba(0x809980ff).into()), + ("number".into(), rgba(0x87711cff).into()), + ("comment.doc".into(), rgba(0x5e6e5eff).into()), + ("tag".into(), rgba(0x3e61f4ff).into()), + ("string.special.symbol".into(), rgba(0x28a328ff).into()), + ("primary".into(), rgba(0x242924ff).into()), + ("string".into(), rgba(0x28a328ff).into()), + ("enum".into(), rgba(0x87711fff).into()), + ("operator".into(), rgba(0x5e6e5eff).into()), + ("string.regex".into(), rgba(0x1899b3ff).into()), + ("keyword".into(), rgba(0xac2aeeff).into()), + ("emphasis".into(), rgba(0x3e61f4ff).into()), + ("link_uri".into(), rgba(0x2aa32aff).into()), + ("constant".into(), rgba(0x2aa32aff).into()), + ("constructor".into(), rgba(0x3e61f4ff).into()), + ("link_text".into(), rgba(0x87711fff).into()), + ("emphasis.strong".into(), rgba(0x3e61f4ff).into()), + ("punctuation.list_marker".into(), rgba(0x242924ff).into()), + ("punctuation.delimiter".into(), rgba(0x5e6e5eff).into()), + ("punctuation.special".into(), rgba(0xe618c3ff).into()), + ("variant".into(), rgba(0x98981bff).into()), + ("predictive".into(), rgba(0x00a2b5ff).into()), + ("attribute".into(), rgba(0x3e61f4ff).into()), + ("preproc".into(), rgba(0x131513ff).into()), + ("embedded".into(), rgba(0x131513ff).into()), + ("punctuation".into(), rgba(0x242924ff).into()), + ("label".into(), rgba(0x3e61f4ff).into()), + ("function.method".into(), rgba(0x3d62f5ff).into()), + ("property".into(), rgba(0xe6183bff).into()), + ("title".into(), rgba(0x131513ff).into()), + ("variable".into(), rgba(0x242924ff).into()), + ("function".into(), rgba(0x3d62f5ff).into()), + ("variable.special".into(), rgba(0xac2aeeff).into()), + ("type".into(), rgba(0x98981bff).into()), + ("text.literal".into(), rgba(0x87711fff).into()), + ("hint".into(), rgba(0x008fa1ff).into()), + ( + "function.special.definition".into(), + rgba(0x98981bff).into(), + ), + ("punctuation.bracket".into(), rgba(0x5e6e5eff).into()), + ], + }, + status_bar: rgba(0xb4ceb4ff).into(), + title_bar: rgba(0xb4ceb4ff).into(), + toolbar: rgba(0xf3faf3ff).into(), + tab_bar: rgba(0xdaeedaff).into(), + editor: rgba(0xf3faf3ff).into(), + editor_subheader: rgba(0xdaeedaff).into(), + editor_active_line: rgba(0xdaeedaff).into(), + terminal: rgba(0xf3faf3ff).into(), + image_fallback_background: rgba(0xb4ceb4ff).into(), + git_created: rgba(0x2aa32aff).into(), + git_modified: rgba(0x3e61f4ff).into(), + git_deleted: rgba(0xe61c3dff).into(), + git_conflict: rgba(0x98981cff).into(), + git_ignored: rgba(0x718771ff).into(), + git_renamed: rgba(0x98981cff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x3e61f4ff).into(), + selection: rgba(0x3e61f43d).into(), + }, + PlayerTheme { + cursor: rgba(0x2aa32aff).into(), + selection: rgba(0x2aa32a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xe61cc2ff).into(), + selection: rgba(0xe61cc23d).into(), + }, + PlayerTheme { + cursor: rgba(0x87711fff).into(), + selection: rgba(0x87711f3d).into(), + }, + PlayerTheme { + cursor: rgba(0xac2dedff).into(), + selection: rgba(0xac2ded3d).into(), + }, + PlayerTheme { + cursor: rgba(0x1c99b3ff).into(), + selection: rgba(0x1c99b33d).into(), + }, + PlayerTheme { + cursor: rgba(0xe61c3dff).into(), + selection: rgba(0xe61c3d3d).into(), + }, + PlayerTheme { + cursor: rgba(0x98981cff).into(), + selection: rgba(0x98981c3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_sulphurpool_dark.rs b/crates/theme2/src/themes/atelier_sulphurpool_dark.rs new file mode 100644 index 0000000000..0c01560f22 --- /dev/null +++ b/crates/theme2/src/themes/atelier_sulphurpool_dark.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_sulphurpool_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Sulphurpool Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x5b6385ff).into(), + border_variant: rgba(0x5b6385ff).into(), + border_focused: rgba(0x203348ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x3e4769ff).into(), + surface: rgba(0x262f51ff).into(), + background: rgba(0x3e4769ff).into(), + filled_element: rgba(0x3e4769ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x161f2bff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x161f2bff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf5f7ffff).into(), + text_muted: rgba(0x959bb2ff).into(), + text_placeholder: rgba(0xc94922ff).into(), + text_disabled: rgba(0x7e849eff).into(), + text_accent: rgba(0x3e8ed0ff).into(), + icon_muted: rgba(0x959bb2ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("title".into(), rgba(0xf5f7ffff).into()), + ("constructor".into(), rgba(0x3e8ed0ff).into()), + ("type".into(), rgba(0xc08b2fff).into()), + ("punctuation.list_marker".into(), rgba(0xdfe2f1ff).into()), + ("property".into(), rgba(0xc94821ff).into()), + ("link_uri".into(), rgba(0xac9739ff).into()), + ("string.escape".into(), rgba(0x979db4ff).into()), + ("constant".into(), rgba(0xac9739ff).into()), + ("embedded".into(), rgba(0xf5f7ffff).into()), + ("punctuation.special".into(), rgba(0x9b6279ff).into()), + ("punctuation.bracket".into(), rgba(0x979db4ff).into()), + ("preproc".into(), rgba(0xf5f7ffff).into()), + ("emphasis.strong".into(), rgba(0x3e8ed0ff).into()), + ("emphasis".into(), rgba(0x3e8ed0ff).into()), + ("enum".into(), rgba(0xc76a29ff).into()), + ("boolean".into(), rgba(0xac9739ff).into()), + ("primary".into(), rgba(0xdfe2f1ff).into()), + ("function.method".into(), rgba(0x3d8fd1ff).into()), + ( + "function.special.definition".into(), + rgba(0xc08b2fff).into(), + ), + ("comment.doc".into(), rgba(0x979db4ff).into()), + ("string".into(), rgba(0xac9738ff).into()), + ("text.literal".into(), rgba(0xc76a29ff).into()), + ("operator".into(), rgba(0x979db4ff).into()), + ("number".into(), rgba(0xc76a28ff).into()), + ("string.special".into(), rgba(0x9b6279ff).into()), + ("punctuation.delimiter".into(), rgba(0x979db4ff).into()), + ("tag".into(), rgba(0x3e8ed0ff).into()), + ("string.special.symbol".into(), rgba(0xac9738ff).into()), + ("variable".into(), rgba(0xdfe2f1ff).into()), + ("attribute".into(), rgba(0x3e8ed0ff).into()), + ("punctuation".into(), rgba(0xdfe2f1ff).into()), + ("string.regex".into(), rgba(0x21a2c9ff).into()), + ("keyword".into(), rgba(0x6679ccff).into()), + ("label".into(), rgba(0x3e8ed0ff).into()), + ("hint".into(), rgba(0x6c81a5ff).into()), + ("function".into(), rgba(0x3d8fd1ff).into()), + ("link_text".into(), rgba(0xc76a29ff).into()), + ("variant".into(), rgba(0xc08b2fff).into()), + ("variable.special".into(), rgba(0x6679ccff).into()), + ("predictive".into(), rgba(0x58709aff).into()), + ("comment".into(), rgba(0x6a7293ff).into()), + ], + }, + status_bar: rgba(0x3e4769ff).into(), + title_bar: rgba(0x3e4769ff).into(), + toolbar: rgba(0x202646ff).into(), + tab_bar: rgba(0x262f51ff).into(), + editor: rgba(0x202646ff).into(), + editor_subheader: rgba(0x262f51ff).into(), + editor_active_line: rgba(0x262f51ff).into(), + terminal: rgba(0x202646ff).into(), + image_fallback_background: rgba(0x3e4769ff).into(), + git_created: rgba(0xac9739ff).into(), + git_modified: rgba(0x3e8ed0ff).into(), + git_deleted: rgba(0xc94922ff).into(), + git_conflict: rgba(0xc08b30ff).into(), + git_ignored: rgba(0x7e849eff).into(), + git_renamed: rgba(0xc08b30ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x3e8ed0ff).into(), + selection: rgba(0x3e8ed03d).into(), + }, + PlayerTheme { + cursor: rgba(0xac9739ff).into(), + selection: rgba(0xac97393d).into(), + }, + PlayerTheme { + cursor: rgba(0x9b6279ff).into(), + selection: rgba(0x9b62793d).into(), + }, + PlayerTheme { + cursor: rgba(0xc76a29ff).into(), + selection: rgba(0xc76a293d).into(), + }, + PlayerTheme { + cursor: rgba(0x6679ccff).into(), + selection: rgba(0x6679cc3d).into(), + }, + PlayerTheme { + cursor: rgba(0x24a1c9ff).into(), + selection: rgba(0x24a1c93d).into(), + }, + PlayerTheme { + cursor: rgba(0xc94922ff).into(), + selection: rgba(0xc949223d).into(), + }, + PlayerTheme { + cursor: rgba(0xc08b30ff).into(), + selection: rgba(0xc08b303d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/atelier_sulphurpool_light.rs b/crates/theme2/src/themes/atelier_sulphurpool_light.rs new file mode 100644 index 0000000000..16b491b6ba --- /dev/null +++ b/crates/theme2/src/themes/atelier_sulphurpool_light.rs @@ -0,0 +1,137 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn atelier_sulphurpool_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Atelier Sulphurpool Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x9a9fb6ff).into(), + border_variant: rgba(0x9a9fb6ff).into(), + border_focused: rgba(0xc2d5efff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xc1c5d8ff).into(), + surface: rgba(0xe5e8f5ff).into(), + background: rgba(0xc1c5d8ff).into(), + filled_element: rgba(0xc1c5d8ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xdde7f6ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xdde7f6ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x202646ff).into(), + text_muted: rgba(0x5f6789ff).into(), + text_placeholder: rgba(0xc94922ff).into(), + text_disabled: rgba(0x767d9aff).into(), + text_accent: rgba(0x3e8fd0ff).into(), + icon_muted: rgba(0x5f6789ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.special".into(), rgba(0x9b6279ff).into()), + ("string.regex".into(), rgba(0x21a2c9ff).into()), + ("embedded".into(), rgba(0x202646ff).into()), + ("string".into(), rgba(0xac9738ff).into()), + ( + "function.special.definition".into(), + rgba(0xc08b2fff).into(), + ), + ("hint".into(), rgba(0x7087b2ff).into()), + ("function.method".into(), rgba(0x3d8fd1ff).into()), + ("punctuation.list_marker".into(), rgba(0x293256ff).into()), + ("punctuation".into(), rgba(0x293256ff).into()), + ("constant".into(), rgba(0xac9739ff).into()), + ("label".into(), rgba(0x3e8fd0ff).into()), + ("comment.doc".into(), rgba(0x5d6587ff).into()), + ("property".into(), rgba(0xc94821ff).into()), + ("punctuation.bracket".into(), rgba(0x5d6587ff).into()), + ("constructor".into(), rgba(0x3e8fd0ff).into()), + ("variable.special".into(), rgba(0x6679ccff).into()), + ("emphasis".into(), rgba(0x3e8fd0ff).into()), + ("link_text".into(), rgba(0xc76a29ff).into()), + ("keyword".into(), rgba(0x6679ccff).into()), + ("primary".into(), rgba(0x293256ff).into()), + ("comment".into(), rgba(0x898ea4ff).into()), + ("title".into(), rgba(0x202646ff).into()), + ("link_uri".into(), rgba(0xac9739ff).into()), + ("text.literal".into(), rgba(0xc76a29ff).into()), + ("operator".into(), rgba(0x5d6587ff).into()), + ("number".into(), rgba(0xc76a28ff).into()), + ("preproc".into(), rgba(0x202646ff).into()), + ("attribute".into(), rgba(0x3e8fd0ff).into()), + ("emphasis.strong".into(), rgba(0x3e8fd0ff).into()), + ("string.escape".into(), rgba(0x5d6587ff).into()), + ("tag".into(), rgba(0x3e8fd0ff).into()), + ("variable".into(), rgba(0x293256ff).into()), + ("predictive".into(), rgba(0x8599beff).into()), + ("enum".into(), rgba(0xc76a29ff).into()), + ("string.special.symbol".into(), rgba(0xac9738ff).into()), + ("punctuation.delimiter".into(), rgba(0x5d6587ff).into()), + ("function".into(), rgba(0x3d8fd1ff).into()), + ("type".into(), rgba(0xc08b2fff).into()), + ("punctuation.special".into(), rgba(0x9b6279ff).into()), + ("variant".into(), rgba(0xc08b2fff).into()), + ("boolean".into(), rgba(0xac9739ff).into()), + ], + }, + status_bar: rgba(0xc1c5d8ff).into(), + title_bar: rgba(0xc1c5d8ff).into(), + toolbar: rgba(0xf5f7ffff).into(), + tab_bar: rgba(0xe5e8f5ff).into(), + editor: rgba(0xf5f7ffff).into(), + editor_subheader: rgba(0xe5e8f5ff).into(), + editor_active_line: rgba(0xe5e8f5ff).into(), + terminal: rgba(0xf5f7ffff).into(), + image_fallback_background: rgba(0xc1c5d8ff).into(), + git_created: rgba(0xac9739ff).into(), + git_modified: rgba(0x3e8fd0ff).into(), + git_deleted: rgba(0xc94922ff).into(), + git_conflict: rgba(0xc08b30ff).into(), + git_ignored: rgba(0x767d9aff).into(), + git_renamed: rgba(0xc08b30ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x3e8fd0ff).into(), + selection: rgba(0x3e8fd03d).into(), + }, + PlayerTheme { + cursor: rgba(0xac9739ff).into(), + selection: rgba(0xac97393d).into(), + }, + PlayerTheme { + cursor: rgba(0x9b6279ff).into(), + selection: rgba(0x9b62793d).into(), + }, + PlayerTheme { + cursor: rgba(0xc76a29ff).into(), + selection: rgba(0xc76a293d).into(), + }, + PlayerTheme { + cursor: rgba(0x6679cbff).into(), + selection: rgba(0x6679cb3d).into(), + }, + PlayerTheme { + cursor: rgba(0x24a1c9ff).into(), + selection: rgba(0x24a1c93d).into(), + }, + PlayerTheme { + cursor: rgba(0xc94922ff).into(), + selection: rgba(0xc949223d).into(), + }, + PlayerTheme { + cursor: rgba(0xc08b30ff).into(), + selection: rgba(0xc08b303d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/ayu_dark.rs b/crates/theme2/src/themes/ayu_dark.rs new file mode 100644 index 0000000000..88f3f93576 --- /dev/null +++ b/crates/theme2/src/themes/ayu_dark.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn ayu_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Ayu Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x3f4043ff).into(), + border_variant: rgba(0x3f4043ff).into(), + border_focused: rgba(0x1b4a6eff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x313337ff).into(), + surface: rgba(0x1f2127ff).into(), + background: rgba(0x313337ff).into(), + filled_element: rgba(0x313337ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x0d2f4eff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x0d2f4eff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xbfbdb6ff).into(), + text_muted: rgba(0x8a8986ff).into(), + text_placeholder: rgba(0xef7177ff).into(), + text_disabled: rgba(0x696a6aff).into(), + text_accent: rgba(0x5ac1feff).into(), + icon_muted: rgba(0x8a8986ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("emphasis".into(), rgba(0x5ac1feff).into()), + ("punctuation.bracket".into(), rgba(0xa6a5a0ff).into()), + ("constructor".into(), rgba(0x5ac1feff).into()), + ("predictive".into(), rgba(0x5a728bff).into()), + ("emphasis.strong".into(), rgba(0x5ac1feff).into()), + ("string.regex".into(), rgba(0x95e6cbff).into()), + ("tag".into(), rgba(0x5ac1feff).into()), + ("punctuation".into(), rgba(0xa6a5a0ff).into()), + ("number".into(), rgba(0xd2a6ffff).into()), + ("punctuation.special".into(), rgba(0xd2a6ffff).into()), + ("primary".into(), rgba(0xbfbdb6ff).into()), + ("boolean".into(), rgba(0xd2a6ffff).into()), + ("variant".into(), rgba(0x5ac1feff).into()), + ("link_uri".into(), rgba(0xaad84cff).into()), + ("comment.doc".into(), rgba(0x8c8b88ff).into()), + ("title".into(), rgba(0xbfbdb6ff).into()), + ("text.literal".into(), rgba(0xfe8f40ff).into()), + ("link_text".into(), rgba(0xfe8f40ff).into()), + ("punctuation.delimiter".into(), rgba(0xa6a5a0ff).into()), + ("string.escape".into(), rgba(0x8c8b88ff).into()), + ("hint".into(), rgba(0x628b80ff).into()), + ("type".into(), rgba(0x59c2ffff).into()), + ("variable".into(), rgba(0xbfbdb6ff).into()), + ("label".into(), rgba(0x5ac1feff).into()), + ("enum".into(), rgba(0xfe8f40ff).into()), + ("operator".into(), rgba(0xf29668ff).into()), + ("function".into(), rgba(0xffb353ff).into()), + ("preproc".into(), rgba(0xbfbdb6ff).into()), + ("embedded".into(), rgba(0xbfbdb6ff).into()), + ("string".into(), rgba(0xa9d94bff).into()), + ("attribute".into(), rgba(0x5ac1feff).into()), + ("keyword".into(), rgba(0xff8f3fff).into()), + ("string.special.symbol".into(), rgba(0xfe8f40ff).into()), + ("comment".into(), rgba(0xabb5be8c).into()), + ("property".into(), rgba(0x5ac1feff).into()), + ("punctuation.list_marker".into(), rgba(0xa6a5a0ff).into()), + ("constant".into(), rgba(0xd2a6ffff).into()), + ("string.special".into(), rgba(0xe5b572ff).into()), + ], + }, + status_bar: rgba(0x313337ff).into(), + title_bar: rgba(0x313337ff).into(), + toolbar: rgba(0x0d1016ff).into(), + tab_bar: rgba(0x1f2127ff).into(), + editor: rgba(0x0d1016ff).into(), + editor_subheader: rgba(0x1f2127ff).into(), + editor_active_line: rgba(0x1f2127ff).into(), + terminal: rgba(0x0d1016ff).into(), + image_fallback_background: rgba(0x313337ff).into(), + git_created: rgba(0xaad84cff).into(), + git_modified: rgba(0x5ac1feff).into(), + git_deleted: rgba(0xef7177ff).into(), + git_conflict: rgba(0xfeb454ff).into(), + git_ignored: rgba(0x696a6aff).into(), + git_renamed: rgba(0xfeb454ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x5ac1feff).into(), + selection: rgba(0x5ac1fe3d).into(), + }, + PlayerTheme { + cursor: rgba(0xaad84cff).into(), + selection: rgba(0xaad84c3d).into(), + }, + PlayerTheme { + cursor: rgba(0x39bae5ff).into(), + selection: rgba(0x39bae53d).into(), + }, + PlayerTheme { + cursor: rgba(0xfe8f40ff).into(), + selection: rgba(0xfe8f403d).into(), + }, + PlayerTheme { + cursor: rgba(0xd2a6feff).into(), + selection: rgba(0xd2a6fe3d).into(), + }, + PlayerTheme { + cursor: rgba(0x95e5cbff).into(), + selection: rgba(0x95e5cb3d).into(), + }, + PlayerTheme { + cursor: rgba(0xef7177ff).into(), + selection: rgba(0xef71773d).into(), + }, + PlayerTheme { + cursor: rgba(0xfeb454ff).into(), + selection: rgba(0xfeb4543d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/ayu_light.rs b/crates/theme2/src/themes/ayu_light.rs new file mode 100644 index 0000000000..761eece82a --- /dev/null +++ b/crates/theme2/src/themes/ayu_light.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn ayu_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Ayu Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xcfd1d2ff).into(), + border_variant: rgba(0xcfd1d2ff).into(), + border_focused: rgba(0xc4daf6ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xdcdddeff).into(), + surface: rgba(0xececedff).into(), + background: rgba(0xdcdddeff).into(), + filled_element: rgba(0xdcdddeff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xdeebfaff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xdeebfaff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x5c6166ff).into(), + text_muted: rgba(0x8b8e92ff).into(), + text_placeholder: rgba(0xef7271ff).into(), + text_disabled: rgba(0xa9acaeff).into(), + text_accent: rgba(0x3b9ee5ff).into(), + icon_muted: rgba(0x8b8e92ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string".into(), rgba(0x86b300ff).into()), + ("enum".into(), rgba(0xf98d3fff).into()), + ("comment".into(), rgba(0x787b8099).into()), + ("comment.doc".into(), rgba(0x898d90ff).into()), + ("emphasis".into(), rgba(0x3b9ee5ff).into()), + ("keyword".into(), rgba(0xfa8d3eff).into()), + ("string.regex".into(), rgba(0x4bbf98ff).into()), + ("text.literal".into(), rgba(0xf98d3fff).into()), + ("string.escape".into(), rgba(0x898d90ff).into()), + ("link_text".into(), rgba(0xf98d3fff).into()), + ("punctuation".into(), rgba(0x73777bff).into()), + ("constructor".into(), rgba(0x3b9ee5ff).into()), + ("constant".into(), rgba(0xa37accff).into()), + ("variable".into(), rgba(0x5c6166ff).into()), + ("primary".into(), rgba(0x5c6166ff).into()), + ("emphasis.strong".into(), rgba(0x3b9ee5ff).into()), + ("string.special".into(), rgba(0xe6ba7eff).into()), + ("number".into(), rgba(0xa37accff).into()), + ("preproc".into(), rgba(0x5c6166ff).into()), + ("punctuation.delimiter".into(), rgba(0x73777bff).into()), + ("string.special.symbol".into(), rgba(0xf98d3fff).into()), + ("boolean".into(), rgba(0xa37accff).into()), + ("property".into(), rgba(0x3b9ee5ff).into()), + ("title".into(), rgba(0x5c6166ff).into()), + ("hint".into(), rgba(0x8ca7c2ff).into()), + ("predictive".into(), rgba(0x9eb9d3ff).into()), + ("operator".into(), rgba(0xed9365ff).into()), + ("type".into(), rgba(0x389ee6ff).into()), + ("function".into(), rgba(0xf2ad48ff).into()), + ("variant".into(), rgba(0x3b9ee5ff).into()), + ("label".into(), rgba(0x3b9ee5ff).into()), + ("punctuation.list_marker".into(), rgba(0x73777bff).into()), + ("punctuation.bracket".into(), rgba(0x73777bff).into()), + ("embedded".into(), rgba(0x5c6166ff).into()), + ("punctuation.special".into(), rgba(0xa37accff).into()), + ("attribute".into(), rgba(0x3b9ee5ff).into()), + ("tag".into(), rgba(0x3b9ee5ff).into()), + ("link_uri".into(), rgba(0x85b304ff).into()), + ], + }, + status_bar: rgba(0xdcdddeff).into(), + title_bar: rgba(0xdcdddeff).into(), + toolbar: rgba(0xfcfcfcff).into(), + tab_bar: rgba(0xececedff).into(), + editor: rgba(0xfcfcfcff).into(), + editor_subheader: rgba(0xececedff).into(), + editor_active_line: rgba(0xececedff).into(), + terminal: rgba(0xfcfcfcff).into(), + image_fallback_background: rgba(0xdcdddeff).into(), + git_created: rgba(0x85b304ff).into(), + git_modified: rgba(0x3b9ee5ff).into(), + git_deleted: rgba(0xef7271ff).into(), + git_conflict: rgba(0xf1ad49ff).into(), + git_ignored: rgba(0xa9acaeff).into(), + git_renamed: rgba(0xf1ad49ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x3b9ee5ff).into(), + selection: rgba(0x3b9ee53d).into(), + }, + PlayerTheme { + cursor: rgba(0x85b304ff).into(), + selection: rgba(0x85b3043d).into(), + }, + PlayerTheme { + cursor: rgba(0x55b4d3ff).into(), + selection: rgba(0x55b4d33d).into(), + }, + PlayerTheme { + cursor: rgba(0xf98d3fff).into(), + selection: rgba(0xf98d3f3d).into(), + }, + PlayerTheme { + cursor: rgba(0xa37accff).into(), + selection: rgba(0xa37acc3d).into(), + }, + PlayerTheme { + cursor: rgba(0x4dbf99ff).into(), + selection: rgba(0x4dbf993d).into(), + }, + PlayerTheme { + cursor: rgba(0xef7271ff).into(), + selection: rgba(0xef72713d).into(), + }, + PlayerTheme { + cursor: rgba(0xf1ad49ff).into(), + selection: rgba(0xf1ad493d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/ayu_mirage.rs b/crates/theme2/src/themes/ayu_mirage.rs new file mode 100644 index 0000000000..cd74529713 --- /dev/null +++ b/crates/theme2/src/themes/ayu_mirage.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn ayu_mirage() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Ayu Mirage".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x53565dff).into(), + border_variant: rgba(0x53565dff).into(), + border_focused: rgba(0x24556fff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x464a52ff).into(), + surface: rgba(0x353944ff).into(), + background: rgba(0x464a52ff).into(), + filled_element: rgba(0x464a52ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x123950ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x123950ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xcccac2ff).into(), + text_muted: rgba(0x9a9a98ff).into(), + text_placeholder: rgba(0xf18779ff).into(), + text_disabled: rgba(0x7b7d7fff).into(), + text_accent: rgba(0x72cffeff).into(), + icon_muted: rgba(0x9a9a98ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("text.literal".into(), rgba(0xfead66ff).into()), + ("link_text".into(), rgba(0xfead66ff).into()), + ("function".into(), rgba(0xffd173ff).into()), + ("punctuation.delimiter".into(), rgba(0xb4b3aeff).into()), + ("property".into(), rgba(0x72cffeff).into()), + ("title".into(), rgba(0xcccac2ff).into()), + ("boolean".into(), rgba(0xdfbfffff).into()), + ("link_uri".into(), rgba(0xd5fe80ff).into()), + ("label".into(), rgba(0x72cffeff).into()), + ("primary".into(), rgba(0xcccac2ff).into()), + ("number".into(), rgba(0xdfbfffff).into()), + ("variant".into(), rgba(0x72cffeff).into()), + ("enum".into(), rgba(0xfead66ff).into()), + ("string.special.symbol".into(), rgba(0xfead66ff).into()), + ("operator".into(), rgba(0xf29e74ff).into()), + ("punctuation.special".into(), rgba(0xdfbfffff).into()), + ("constructor".into(), rgba(0x72cffeff).into()), + ("type".into(), rgba(0x73cfffff).into()), + ("emphasis.strong".into(), rgba(0x72cffeff).into()), + ("embedded".into(), rgba(0xcccac2ff).into()), + ("comment".into(), rgba(0xb8cfe680).into()), + ("tag".into(), rgba(0x72cffeff).into()), + ("keyword".into(), rgba(0xffad65ff).into()), + ("punctuation".into(), rgba(0xb4b3aeff).into()), + ("preproc".into(), rgba(0xcccac2ff).into()), + ("hint".into(), rgba(0x7399a3ff).into()), + ("string.special".into(), rgba(0xffdfb3ff).into()), + ("attribute".into(), rgba(0x72cffeff).into()), + ("string.regex".into(), rgba(0x95e6cbff).into()), + ("predictive".into(), rgba(0x6d839bff).into()), + ("comment.doc".into(), rgba(0x9b9b99ff).into()), + ("emphasis".into(), rgba(0x72cffeff).into()), + ("string".into(), rgba(0xd4fe7fff).into()), + ("constant".into(), rgba(0xdfbfffff).into()), + ("string.escape".into(), rgba(0x9b9b99ff).into()), + ("variable".into(), rgba(0xcccac2ff).into()), + ("punctuation.bracket".into(), rgba(0xb4b3aeff).into()), + ("punctuation.list_marker".into(), rgba(0xb4b3aeff).into()), + ], + }, + status_bar: rgba(0x464a52ff).into(), + title_bar: rgba(0x464a52ff).into(), + toolbar: rgba(0x242835ff).into(), + tab_bar: rgba(0x353944ff).into(), + editor: rgba(0x242835ff).into(), + editor_subheader: rgba(0x353944ff).into(), + editor_active_line: rgba(0x353944ff).into(), + terminal: rgba(0x242835ff).into(), + image_fallback_background: rgba(0x464a52ff).into(), + git_created: rgba(0xd5fe80ff).into(), + git_modified: rgba(0x72cffeff).into(), + git_deleted: rgba(0xf18779ff).into(), + git_conflict: rgba(0xfecf72ff).into(), + git_ignored: rgba(0x7b7d7fff).into(), + git_renamed: rgba(0xfecf72ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x72cffeff).into(), + selection: rgba(0x72cffe3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd5fe80ff).into(), + selection: rgba(0xd5fe803d).into(), + }, + PlayerTheme { + cursor: rgba(0x5bcde5ff).into(), + selection: rgba(0x5bcde53d).into(), + }, + PlayerTheme { + cursor: rgba(0xfead66ff).into(), + selection: rgba(0xfead663d).into(), + }, + PlayerTheme { + cursor: rgba(0xdebffeff).into(), + selection: rgba(0xdebffe3d).into(), + }, + PlayerTheme { + cursor: rgba(0x95e5cbff).into(), + selection: rgba(0x95e5cb3d).into(), + }, + PlayerTheme { + cursor: rgba(0xf18779ff).into(), + selection: rgba(0xf187793d).into(), + }, + PlayerTheme { + cursor: rgba(0xfecf72ff).into(), + selection: rgba(0xfecf723d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/gruvbox_dark.rs b/crates/theme2/src/themes/gruvbox_dark.rs new file mode 100644 index 0000000000..1f32e820c9 --- /dev/null +++ b/crates/theme2/src/themes/gruvbox_dark.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn gruvbox_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Gruvbox Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x5b534dff).into(), + border_variant: rgba(0x5b534dff).into(), + border_focused: rgba(0x303a36ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x4c4642ff).into(), + surface: rgba(0x3a3735ff).into(), + background: rgba(0x4c4642ff).into(), + filled_element: rgba(0x4c4642ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x1e2321ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x1e2321ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfbf1c7ff).into(), + text_muted: rgba(0xc5b597ff).into(), + text_placeholder: rgba(0xfb4a35ff).into(), + text_disabled: rgba(0x998b78ff).into(), + text_accent: rgba(0x83a598ff).into(), + icon_muted: rgba(0xc5b597ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("operator".into(), rgba(0x8ec07cff).into()), + ("string.special.symbol".into(), rgba(0x8ec07cff).into()), + ("emphasis.strong".into(), rgba(0x83a598ff).into()), + ("attribute".into(), rgba(0x83a598ff).into()), + ("property".into(), rgba(0xebdbb2ff).into()), + ("comment.doc".into(), rgba(0xc6b697ff).into()), + ("emphasis".into(), rgba(0x83a598ff).into()), + ("variant".into(), rgba(0x83a598ff).into()), + ("text.literal".into(), rgba(0x83a598ff).into()), + ("keyword".into(), rgba(0xfb4833ff).into()), + ("primary".into(), rgba(0xebdbb2ff).into()), + ("variable".into(), rgba(0x83a598ff).into()), + ("enum".into(), rgba(0xfe7f18ff).into()), + ("constructor".into(), rgba(0x83a598ff).into()), + ("punctuation".into(), rgba(0xd5c4a1ff).into()), + ("link_uri".into(), rgba(0xd3869bff).into()), + ("hint".into(), rgba(0x8c957dff).into()), + ("string.regex".into(), rgba(0xfe7f18ff).into()), + ("punctuation.delimiter".into(), rgba(0xe5d5adff).into()), + ("string".into(), rgba(0xb8bb25ff).into()), + ("punctuation.special".into(), rgba(0xe5d5adff).into()), + ("link_text".into(), rgba(0x8ec07cff).into()), + ("tag".into(), rgba(0x8ec07cff).into()), + ("string.escape".into(), rgba(0xc6b697ff).into()), + ("label".into(), rgba(0x83a598ff).into()), + ("constant".into(), rgba(0xfabd2eff).into()), + ("type".into(), rgba(0xfabd2eff).into()), + ("number".into(), rgba(0xd3869bff).into()), + ("string.special".into(), rgba(0xd3869bff).into()), + ("function.builtin".into(), rgba(0xfb4833ff).into()), + ("boolean".into(), rgba(0xd3869bff).into()), + ("embedded".into(), rgba(0x8ec07cff).into()), + ("title".into(), rgba(0xb8bb25ff).into()), + ("function".into(), rgba(0xb8bb25ff).into()), + ("punctuation.bracket".into(), rgba(0xa89984ff).into()), + ("comment".into(), rgba(0xa89984ff).into()), + ("preproc".into(), rgba(0xfbf1c7ff).into()), + ("predictive".into(), rgba(0x717363ff).into()), + ("punctuation.list_marker".into(), rgba(0xebdbb2ff).into()), + ], + }, + status_bar: rgba(0x4c4642ff).into(), + title_bar: rgba(0x4c4642ff).into(), + toolbar: rgba(0x282828ff).into(), + tab_bar: rgba(0x3a3735ff).into(), + editor: rgba(0x282828ff).into(), + editor_subheader: rgba(0x3a3735ff).into(), + editor_active_line: rgba(0x3a3735ff).into(), + terminal: rgba(0x282828ff).into(), + image_fallback_background: rgba(0x4c4642ff).into(), + git_created: rgba(0xb7bb26ff).into(), + git_modified: rgba(0x83a598ff).into(), + git_deleted: rgba(0xfb4a35ff).into(), + git_conflict: rgba(0xf9bd2fff).into(), + git_ignored: rgba(0x998b78ff).into(), + git_renamed: rgba(0xf9bd2fff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), + }, + PlayerTheme { + cursor: rgba(0xb7bb26ff).into(), + selection: rgba(0xb7bb263d).into(), + }, + PlayerTheme { + cursor: rgba(0xa89984ff).into(), + selection: rgba(0xa899843d).into(), + }, + PlayerTheme { + cursor: rgba(0xfd801bff).into(), + selection: rgba(0xfd801b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd3869bff).into(), + selection: rgba(0xd3869b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x8ec07cff).into(), + selection: rgba(0x8ec07c3d).into(), + }, + PlayerTheme { + cursor: rgba(0xfb4a35ff).into(), + selection: rgba(0xfb4a353d).into(), + }, + PlayerTheme { + cursor: rgba(0xf9bd2fff).into(), + selection: rgba(0xf9bd2f3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/gruvbox_dark_hard.rs b/crates/theme2/src/themes/gruvbox_dark_hard.rs new file mode 100644 index 0000000000..cf7875b33e --- /dev/null +++ b/crates/theme2/src/themes/gruvbox_dark_hard.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn gruvbox_dark_hard() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Gruvbox Dark Hard".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x5b534dff).into(), + border_variant: rgba(0x5b534dff).into(), + border_focused: rgba(0x303a36ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x4c4642ff).into(), + surface: rgba(0x393634ff).into(), + background: rgba(0x4c4642ff).into(), + filled_element: rgba(0x4c4642ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x1e2321ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x1e2321ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfbf1c7ff).into(), + text_muted: rgba(0xc5b597ff).into(), + text_placeholder: rgba(0xfb4a35ff).into(), + text_disabled: rgba(0x998b78ff).into(), + text_accent: rgba(0x83a598ff).into(), + icon_muted: rgba(0xc5b597ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("primary".into(), rgba(0xebdbb2ff).into()), + ("label".into(), rgba(0x83a598ff).into()), + ("punctuation.delimiter".into(), rgba(0xe5d5adff).into()), + ("variant".into(), rgba(0x83a598ff).into()), + ("type".into(), rgba(0xfabd2eff).into()), + ("string.regex".into(), rgba(0xfe7f18ff).into()), + ("function.builtin".into(), rgba(0xfb4833ff).into()), + ("title".into(), rgba(0xb8bb25ff).into()), + ("string".into(), rgba(0xb8bb25ff).into()), + ("operator".into(), rgba(0x8ec07cff).into()), + ("embedded".into(), rgba(0x8ec07cff).into()), + ("punctuation.bracket".into(), rgba(0xa89984ff).into()), + ("string.special".into(), rgba(0xd3869bff).into()), + ("attribute".into(), rgba(0x83a598ff).into()), + ("comment".into(), rgba(0xa89984ff).into()), + ("link_text".into(), rgba(0x8ec07cff).into()), + ("punctuation.special".into(), rgba(0xe5d5adff).into()), + ("punctuation.list_marker".into(), rgba(0xebdbb2ff).into()), + ("comment.doc".into(), rgba(0xc6b697ff).into()), + ("preproc".into(), rgba(0xfbf1c7ff).into()), + ("text.literal".into(), rgba(0x83a598ff).into()), + ("function".into(), rgba(0xb8bb25ff).into()), + ("predictive".into(), rgba(0x717363ff).into()), + ("emphasis.strong".into(), rgba(0x83a598ff).into()), + ("punctuation".into(), rgba(0xd5c4a1ff).into()), + ("string.special.symbol".into(), rgba(0x8ec07cff).into()), + ("property".into(), rgba(0xebdbb2ff).into()), + ("keyword".into(), rgba(0xfb4833ff).into()), + ("constructor".into(), rgba(0x83a598ff).into()), + ("tag".into(), rgba(0x8ec07cff).into()), + ("variable".into(), rgba(0x83a598ff).into()), + ("enum".into(), rgba(0xfe7f18ff).into()), + ("hint".into(), rgba(0x8c957dff).into()), + ("number".into(), rgba(0xd3869bff).into()), + ("constant".into(), rgba(0xfabd2eff).into()), + ("boolean".into(), rgba(0xd3869bff).into()), + ("link_uri".into(), rgba(0xd3869bff).into()), + ("string.escape".into(), rgba(0xc6b697ff).into()), + ("emphasis".into(), rgba(0x83a598ff).into()), + ], + }, + status_bar: rgba(0x4c4642ff).into(), + title_bar: rgba(0x4c4642ff).into(), + toolbar: rgba(0x1d2021ff).into(), + tab_bar: rgba(0x393634ff).into(), + editor: rgba(0x1d2021ff).into(), + editor_subheader: rgba(0x393634ff).into(), + editor_active_line: rgba(0x393634ff).into(), + terminal: rgba(0x1d2021ff).into(), + image_fallback_background: rgba(0x4c4642ff).into(), + git_created: rgba(0xb7bb26ff).into(), + git_modified: rgba(0x83a598ff).into(), + git_deleted: rgba(0xfb4a35ff).into(), + git_conflict: rgba(0xf9bd2fff).into(), + git_ignored: rgba(0x998b78ff).into(), + git_renamed: rgba(0xf9bd2fff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), + }, + PlayerTheme { + cursor: rgba(0xb7bb26ff).into(), + selection: rgba(0xb7bb263d).into(), + }, + PlayerTheme { + cursor: rgba(0xa89984ff).into(), + selection: rgba(0xa899843d).into(), + }, + PlayerTheme { + cursor: rgba(0xfd801bff).into(), + selection: rgba(0xfd801b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd3869bff).into(), + selection: rgba(0xd3869b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x8ec07cff).into(), + selection: rgba(0x8ec07c3d).into(), + }, + PlayerTheme { + cursor: rgba(0xfb4a35ff).into(), + selection: rgba(0xfb4a353d).into(), + }, + PlayerTheme { + cursor: rgba(0xf9bd2fff).into(), + selection: rgba(0xf9bd2f3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/gruvbox_dark_soft.rs b/crates/theme2/src/themes/gruvbox_dark_soft.rs new file mode 100644 index 0000000000..f0e1c44e30 --- /dev/null +++ b/crates/theme2/src/themes/gruvbox_dark_soft.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn gruvbox_dark_soft() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Gruvbox Dark Soft".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x5b534dff).into(), + border_variant: rgba(0x5b534dff).into(), + border_focused: rgba(0x303a36ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x4c4642ff).into(), + surface: rgba(0x3b3735ff).into(), + background: rgba(0x4c4642ff).into(), + filled_element: rgba(0x4c4642ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x1e2321ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x1e2321ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfbf1c7ff).into(), + text_muted: rgba(0xc5b597ff).into(), + text_placeholder: rgba(0xfb4a35ff).into(), + text_disabled: rgba(0x998b78ff).into(), + text_accent: rgba(0x83a598ff).into(), + icon_muted: rgba(0xc5b597ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("punctuation.special".into(), rgba(0xe5d5adff).into()), + ("attribute".into(), rgba(0x83a598ff).into()), + ("preproc".into(), rgba(0xfbf1c7ff).into()), + ("keyword".into(), rgba(0xfb4833ff).into()), + ("emphasis".into(), rgba(0x83a598ff).into()), + ("punctuation.delimiter".into(), rgba(0xe5d5adff).into()), + ("punctuation.bracket".into(), rgba(0xa89984ff).into()), + ("comment".into(), rgba(0xa89984ff).into()), + ("text.literal".into(), rgba(0x83a598ff).into()), + ("predictive".into(), rgba(0x717363ff).into()), + ("link_text".into(), rgba(0x8ec07cff).into()), + ("variant".into(), rgba(0x83a598ff).into()), + ("label".into(), rgba(0x83a598ff).into()), + ("function".into(), rgba(0xb8bb25ff).into()), + ("string.regex".into(), rgba(0xfe7f18ff).into()), + ("boolean".into(), rgba(0xd3869bff).into()), + ("number".into(), rgba(0xd3869bff).into()), + ("string.escape".into(), rgba(0xc6b697ff).into()), + ("constructor".into(), rgba(0x83a598ff).into()), + ("link_uri".into(), rgba(0xd3869bff).into()), + ("string.special.symbol".into(), rgba(0x8ec07cff).into()), + ("type".into(), rgba(0xfabd2eff).into()), + ("function.builtin".into(), rgba(0xfb4833ff).into()), + ("title".into(), rgba(0xb8bb25ff).into()), + ("primary".into(), rgba(0xebdbb2ff).into()), + ("tag".into(), rgba(0x8ec07cff).into()), + ("constant".into(), rgba(0xfabd2eff).into()), + ("emphasis.strong".into(), rgba(0x83a598ff).into()), + ("string.special".into(), rgba(0xd3869bff).into()), + ("hint".into(), rgba(0x8c957dff).into()), + ("comment.doc".into(), rgba(0xc6b697ff).into()), + ("property".into(), rgba(0xebdbb2ff).into()), + ("embedded".into(), rgba(0x8ec07cff).into()), + ("operator".into(), rgba(0x8ec07cff).into()), + ("punctuation".into(), rgba(0xd5c4a1ff).into()), + ("variable".into(), rgba(0x83a598ff).into()), + ("enum".into(), rgba(0xfe7f18ff).into()), + ("punctuation.list_marker".into(), rgba(0xebdbb2ff).into()), + ("string".into(), rgba(0xb8bb25ff).into()), + ], + }, + status_bar: rgba(0x4c4642ff).into(), + title_bar: rgba(0x4c4642ff).into(), + toolbar: rgba(0x32302fff).into(), + tab_bar: rgba(0x3b3735ff).into(), + editor: rgba(0x32302fff).into(), + editor_subheader: rgba(0x3b3735ff).into(), + editor_active_line: rgba(0x3b3735ff).into(), + terminal: rgba(0x32302fff).into(), + image_fallback_background: rgba(0x4c4642ff).into(), + git_created: rgba(0xb7bb26ff).into(), + git_modified: rgba(0x83a598ff).into(), + git_deleted: rgba(0xfb4a35ff).into(), + git_conflict: rgba(0xf9bd2fff).into(), + git_ignored: rgba(0x998b78ff).into(), + git_renamed: rgba(0xf9bd2fff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x83a598ff).into(), + selection: rgba(0x83a5983d).into(), + }, + PlayerTheme { + cursor: rgba(0xb7bb26ff).into(), + selection: rgba(0xb7bb263d).into(), + }, + PlayerTheme { + cursor: rgba(0xa89984ff).into(), + selection: rgba(0xa899843d).into(), + }, + PlayerTheme { + cursor: rgba(0xfd801bff).into(), + selection: rgba(0xfd801b3d).into(), + }, + PlayerTheme { + cursor: rgba(0xd3869bff).into(), + selection: rgba(0xd3869b3d).into(), + }, + PlayerTheme { + cursor: rgba(0x8ec07cff).into(), + selection: rgba(0x8ec07c3d).into(), + }, + PlayerTheme { + cursor: rgba(0xfb4a35ff).into(), + selection: rgba(0xfb4a353d).into(), + }, + PlayerTheme { + cursor: rgba(0xf9bd2fff).into(), + selection: rgba(0xf9bd2f3d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/gruvbox_light.rs b/crates/theme2/src/themes/gruvbox_light.rs new file mode 100644 index 0000000000..76e35bd0b6 --- /dev/null +++ b/crates/theme2/src/themes/gruvbox_light.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn gruvbox_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Gruvbox Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xc8b899ff).into(), + border_variant: rgba(0xc8b899ff).into(), + border_focused: rgba(0xadc5ccff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xd9c8a4ff).into(), + surface: rgba(0xecddb4ff).into(), + background: rgba(0xd9c8a4ff).into(), + filled_element: rgba(0xd9c8a4ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xd2dee2ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xd2dee2ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x282828ff).into(), + text_muted: rgba(0x5f5650ff).into(), + text_placeholder: rgba(0x9d0308ff).into(), + text_disabled: rgba(0x897b6eff).into(), + text_accent: rgba(0x0b6678ff).into(), + icon_muted: rgba(0x5f5650ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("number".into(), rgba(0x8f3e71ff).into()), + ("link_text".into(), rgba(0x427b58ff).into()), + ("string.special".into(), rgba(0x8f3e71ff).into()), + ("string.special.symbol".into(), rgba(0x427b58ff).into()), + ("function".into(), rgba(0x79740eff).into()), + ("title".into(), rgba(0x79740eff).into()), + ("emphasis".into(), rgba(0x0b6678ff).into()), + ("punctuation".into(), rgba(0x3c3836ff).into()), + ("string.escape".into(), rgba(0x5d544eff).into()), + ("type".into(), rgba(0xb57613ff).into()), + ("string".into(), rgba(0x79740eff).into()), + ("keyword".into(), rgba(0x9d0006ff).into()), + ("tag".into(), rgba(0x427b58ff).into()), + ("primary".into(), rgba(0x282828ff).into()), + ("link_uri".into(), rgba(0x8f3e71ff).into()), + ("comment.doc".into(), rgba(0x5d544eff).into()), + ("boolean".into(), rgba(0x8f3e71ff).into()), + ("embedded".into(), rgba(0x427b58ff).into()), + ("hint".into(), rgba(0x677562ff).into()), + ("emphasis.strong".into(), rgba(0x0b6678ff).into()), + ("operator".into(), rgba(0x427b58ff).into()), + ("label".into(), rgba(0x0b6678ff).into()), + ("comment".into(), rgba(0x7c6f64ff).into()), + ("function.builtin".into(), rgba(0x9d0006ff).into()), + ("punctuation.bracket".into(), rgba(0x665c54ff).into()), + ("text.literal".into(), rgba(0x066578ff).into()), + ("string.regex".into(), rgba(0xaf3a02ff).into()), + ("property".into(), rgba(0x282828ff).into()), + ("attribute".into(), rgba(0x0b6678ff).into()), + ("punctuation.delimiter".into(), rgba(0x413d3aff).into()), + ("constructor".into(), rgba(0x0b6678ff).into()), + ("variable".into(), rgba(0x066578ff).into()), + ("constant".into(), rgba(0xb57613ff).into()), + ("preproc".into(), rgba(0x282828ff).into()), + ("punctuation.special".into(), rgba(0x413d3aff).into()), + ("punctuation.list_marker".into(), rgba(0x282828ff).into()), + ("variant".into(), rgba(0x0b6678ff).into()), + ("predictive".into(), rgba(0x7c9780ff).into()), + ("enum".into(), rgba(0xaf3a02ff).into()), + ], + }, + status_bar: rgba(0xd9c8a4ff).into(), + title_bar: rgba(0xd9c8a4ff).into(), + toolbar: rgba(0xfbf1c7ff).into(), + tab_bar: rgba(0xecddb4ff).into(), + editor: rgba(0xfbf1c7ff).into(), + editor_subheader: rgba(0xecddb4ff).into(), + editor_active_line: rgba(0xecddb4ff).into(), + terminal: rgba(0xfbf1c7ff).into(), + image_fallback_background: rgba(0xd9c8a4ff).into(), + git_created: rgba(0x797410ff).into(), + git_modified: rgba(0x0b6678ff).into(), + git_deleted: rgba(0x9d0308ff).into(), + git_conflict: rgba(0xb57615ff).into(), + git_ignored: rgba(0x897b6eff).into(), + git_renamed: rgba(0xb57615ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x0b6678ff).into(), + selection: rgba(0x0b66783d).into(), + }, + PlayerTheme { + cursor: rgba(0x797410ff).into(), + selection: rgba(0x7974103d).into(), + }, + PlayerTheme { + cursor: rgba(0x7c6f64ff).into(), + selection: rgba(0x7c6f643d).into(), + }, + PlayerTheme { + cursor: rgba(0xaf3a04ff).into(), + selection: rgba(0xaf3a043d).into(), + }, + PlayerTheme { + cursor: rgba(0x8f3f70ff).into(), + selection: rgba(0x8f3f703d).into(), + }, + PlayerTheme { + cursor: rgba(0x437b59ff).into(), + selection: rgba(0x437b593d).into(), + }, + PlayerTheme { + cursor: rgba(0x9d0308ff).into(), + selection: rgba(0x9d03083d).into(), + }, + PlayerTheme { + cursor: rgba(0xb57615ff).into(), + selection: rgba(0xb576153d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/gruvbox_light_hard.rs b/crates/theme2/src/themes/gruvbox_light_hard.rs new file mode 100644 index 0000000000..8438e0f893 --- /dev/null +++ b/crates/theme2/src/themes/gruvbox_light_hard.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn gruvbox_light_hard() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Gruvbox Light Hard".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xc8b899ff).into(), + border_variant: rgba(0xc8b899ff).into(), + border_focused: rgba(0xadc5ccff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xd9c8a4ff).into(), + surface: rgba(0xecddb5ff).into(), + background: rgba(0xd9c8a4ff).into(), + filled_element: rgba(0xd9c8a4ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xd2dee2ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xd2dee2ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x282828ff).into(), + text_muted: rgba(0x5f5650ff).into(), + text_placeholder: rgba(0x9d0308ff).into(), + text_disabled: rgba(0x897b6eff).into(), + text_accent: rgba(0x0b6678ff).into(), + icon_muted: rgba(0x5f5650ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("label".into(), rgba(0x0b6678ff).into()), + ("hint".into(), rgba(0x677562ff).into()), + ("boolean".into(), rgba(0x8f3e71ff).into()), + ("function.builtin".into(), rgba(0x9d0006ff).into()), + ("constant".into(), rgba(0xb57613ff).into()), + ("preproc".into(), rgba(0x282828ff).into()), + ("predictive".into(), rgba(0x7c9780ff).into()), + ("string".into(), rgba(0x79740eff).into()), + ("comment.doc".into(), rgba(0x5d544eff).into()), + ("function".into(), rgba(0x79740eff).into()), + ("title".into(), rgba(0x79740eff).into()), + ("text.literal".into(), rgba(0x066578ff).into()), + ("punctuation.bracket".into(), rgba(0x665c54ff).into()), + ("string.escape".into(), rgba(0x5d544eff).into()), + ("punctuation.delimiter".into(), rgba(0x413d3aff).into()), + ("string.special.symbol".into(), rgba(0x427b58ff).into()), + ("type".into(), rgba(0xb57613ff).into()), + ("constructor".into(), rgba(0x0b6678ff).into()), + ("property".into(), rgba(0x282828ff).into()), + ("comment".into(), rgba(0x7c6f64ff).into()), + ("enum".into(), rgba(0xaf3a02ff).into()), + ("emphasis".into(), rgba(0x0b6678ff).into()), + ("embedded".into(), rgba(0x427b58ff).into()), + ("operator".into(), rgba(0x427b58ff).into()), + ("attribute".into(), rgba(0x0b6678ff).into()), + ("emphasis.strong".into(), rgba(0x0b6678ff).into()), + ("link_text".into(), rgba(0x427b58ff).into()), + ("punctuation.special".into(), rgba(0x413d3aff).into()), + ("punctuation.list_marker".into(), rgba(0x282828ff).into()), + ("variant".into(), rgba(0x0b6678ff).into()), + ("primary".into(), rgba(0x282828ff).into()), + ("number".into(), rgba(0x8f3e71ff).into()), + ("tag".into(), rgba(0x427b58ff).into()), + ("keyword".into(), rgba(0x9d0006ff).into()), + ("link_uri".into(), rgba(0x8f3e71ff).into()), + ("string.regex".into(), rgba(0xaf3a02ff).into()), + ("variable".into(), rgba(0x066578ff).into()), + ("string.special".into(), rgba(0x8f3e71ff).into()), + ("punctuation".into(), rgba(0x3c3836ff).into()), + ], + }, + status_bar: rgba(0xd9c8a4ff).into(), + title_bar: rgba(0xd9c8a4ff).into(), + toolbar: rgba(0xf9f5d7ff).into(), + tab_bar: rgba(0xecddb5ff).into(), + editor: rgba(0xf9f5d7ff).into(), + editor_subheader: rgba(0xecddb5ff).into(), + editor_active_line: rgba(0xecddb5ff).into(), + terminal: rgba(0xf9f5d7ff).into(), + image_fallback_background: rgba(0xd9c8a4ff).into(), + git_created: rgba(0x797410ff).into(), + git_modified: rgba(0x0b6678ff).into(), + git_deleted: rgba(0x9d0308ff).into(), + git_conflict: rgba(0xb57615ff).into(), + git_ignored: rgba(0x897b6eff).into(), + git_renamed: rgba(0xb57615ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x0b6678ff).into(), + selection: rgba(0x0b66783d).into(), + }, + PlayerTheme { + cursor: rgba(0x797410ff).into(), + selection: rgba(0x7974103d).into(), + }, + PlayerTheme { + cursor: rgba(0x7c6f64ff).into(), + selection: rgba(0x7c6f643d).into(), + }, + PlayerTheme { + cursor: rgba(0xaf3a04ff).into(), + selection: rgba(0xaf3a043d).into(), + }, + PlayerTheme { + cursor: rgba(0x8f3f70ff).into(), + selection: rgba(0x8f3f703d).into(), + }, + PlayerTheme { + cursor: rgba(0x437b59ff).into(), + selection: rgba(0x437b593d).into(), + }, + PlayerTheme { + cursor: rgba(0x9d0308ff).into(), + selection: rgba(0x9d03083d).into(), + }, + PlayerTheme { + cursor: rgba(0xb57615ff).into(), + selection: rgba(0xb576153d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/gruvbox_light_soft.rs b/crates/theme2/src/themes/gruvbox_light_soft.rs new file mode 100644 index 0000000000..d420b580f8 --- /dev/null +++ b/crates/theme2/src/themes/gruvbox_light_soft.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn gruvbox_light_soft() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Gruvbox Light Soft".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xc8b899ff).into(), + border_variant: rgba(0xc8b899ff).into(), + border_focused: rgba(0xadc5ccff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xd9c8a4ff).into(), + surface: rgba(0xecdcb3ff).into(), + background: rgba(0xd9c8a4ff).into(), + filled_element: rgba(0xd9c8a4ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xd2dee2ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xd2dee2ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x282828ff).into(), + text_muted: rgba(0x5f5650ff).into(), + text_placeholder: rgba(0x9d0308ff).into(), + text_disabled: rgba(0x897b6eff).into(), + text_accent: rgba(0x0b6678ff).into(), + icon_muted: rgba(0x5f5650ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("preproc".into(), rgba(0x282828ff).into()), + ("punctuation.list_marker".into(), rgba(0x282828ff).into()), + ("string".into(), rgba(0x79740eff).into()), + ("constant".into(), rgba(0xb57613ff).into()), + ("keyword".into(), rgba(0x9d0006ff).into()), + ("string.special.symbol".into(), rgba(0x427b58ff).into()), + ("comment.doc".into(), rgba(0x5d544eff).into()), + ("hint".into(), rgba(0x677562ff).into()), + ("number".into(), rgba(0x8f3e71ff).into()), + ("enum".into(), rgba(0xaf3a02ff).into()), + ("emphasis".into(), rgba(0x0b6678ff).into()), + ("operator".into(), rgba(0x427b58ff).into()), + ("comment".into(), rgba(0x7c6f64ff).into()), + ("embedded".into(), rgba(0x427b58ff).into()), + ("type".into(), rgba(0xb57613ff).into()), + ("title".into(), rgba(0x79740eff).into()), + ("constructor".into(), rgba(0x0b6678ff).into()), + ("punctuation.delimiter".into(), rgba(0x413d3aff).into()), + ("function".into(), rgba(0x79740eff).into()), + ("link_uri".into(), rgba(0x8f3e71ff).into()), + ("emphasis.strong".into(), rgba(0x0b6678ff).into()), + ("boolean".into(), rgba(0x8f3e71ff).into()), + ("function.builtin".into(), rgba(0x9d0006ff).into()), + ("predictive".into(), rgba(0x7c9780ff).into()), + ("string.regex".into(), rgba(0xaf3a02ff).into()), + ("tag".into(), rgba(0x427b58ff).into()), + ("text.literal".into(), rgba(0x066578ff).into()), + ("punctuation".into(), rgba(0x3c3836ff).into()), + ("punctuation.bracket".into(), rgba(0x665c54ff).into()), + ("variable".into(), rgba(0x066578ff).into()), + ("attribute".into(), rgba(0x0b6678ff).into()), + ("string.special".into(), rgba(0x8f3e71ff).into()), + ("label".into(), rgba(0x0b6678ff).into()), + ("string.escape".into(), rgba(0x5d544eff).into()), + ("link_text".into(), rgba(0x427b58ff).into()), + ("punctuation.special".into(), rgba(0x413d3aff).into()), + ("property".into(), rgba(0x282828ff).into()), + ("variant".into(), rgba(0x0b6678ff).into()), + ("primary".into(), rgba(0x282828ff).into()), + ], + }, + status_bar: rgba(0xd9c8a4ff).into(), + title_bar: rgba(0xd9c8a4ff).into(), + toolbar: rgba(0xf2e5bcff).into(), + tab_bar: rgba(0xecdcb3ff).into(), + editor: rgba(0xf2e5bcff).into(), + editor_subheader: rgba(0xecdcb3ff).into(), + editor_active_line: rgba(0xecdcb3ff).into(), + terminal: rgba(0xf2e5bcff).into(), + image_fallback_background: rgba(0xd9c8a4ff).into(), + git_created: rgba(0x797410ff).into(), + git_modified: rgba(0x0b6678ff).into(), + git_deleted: rgba(0x9d0308ff).into(), + git_conflict: rgba(0xb57615ff).into(), + git_ignored: rgba(0x897b6eff).into(), + git_renamed: rgba(0xb57615ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x0b6678ff).into(), + selection: rgba(0x0b66783d).into(), + }, + PlayerTheme { + cursor: rgba(0x797410ff).into(), + selection: rgba(0x7974103d).into(), + }, + PlayerTheme { + cursor: rgba(0x7c6f64ff).into(), + selection: rgba(0x7c6f643d).into(), + }, + PlayerTheme { + cursor: rgba(0xaf3a04ff).into(), + selection: rgba(0xaf3a043d).into(), + }, + PlayerTheme { + cursor: rgba(0x8f3f70ff).into(), + selection: rgba(0x8f3f703d).into(), + }, + PlayerTheme { + cursor: rgba(0x437b59ff).into(), + selection: rgba(0x437b593d).into(), + }, + PlayerTheme { + cursor: rgba(0x9d0308ff).into(), + selection: rgba(0x9d03083d).into(), + }, + PlayerTheme { + cursor: rgba(0xb57615ff).into(), + selection: rgba(0xb576153d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/mod.rs b/crates/theme2/src/themes/mod.rs index 63a895c98c..018ec7c939 100644 --- a/crates/theme2/src/themes/mod.rs +++ b/crates/theme2/src/themes/mod.rs @@ -1,7 +1,80 @@ -mod one_dark; -mod rose_pine; -mod sandcastle; +mod andromeda; +mod atelier_cave_dark; +mod atelier_cave_light; +mod atelier_dune_dark; +mod atelier_dune_light; +mod atelier_estuary_dark; +mod atelier_estuary_light; +mod atelier_forest_dark; +mod atelier_forest_light; +mod atelier_heath_dark; +mod atelier_heath_light; +mod atelier_lakeside_dark; +mod atelier_lakeside_light; +mod atelier_plateau_dark; +mod atelier_plateau_light; +mod atelier_savanna_dark; +mod atelier_savanna_light; +mod atelier_seaside_dark; +mod atelier_seaside_light; +mod atelier_sulphurpool_dark; +mod atelier_sulphurpool_light; +mod ayu_dark; +mod ayu_light; +mod ayu_mirage; +mod gruvbox_dark; +mod gruvbox_dark_hard; +mod gruvbox_dark_soft; +mod gruvbox_light; +mod gruvbox_light_hard; +mod gruvbox_light_soft; +mod one_dark; +mod one_light; +mod rose_pine; +mod rose_pine_dawn; +mod rose_pine_moon; +mod sandcastle; +mod solarized_dark; +mod solarized_light; +mod summercamp; + +pub use andromeda::*; +pub use atelier_cave_dark::*; +pub use atelier_cave_light::*; +pub use atelier_dune_dark::*; +pub use atelier_dune_light::*; +pub use atelier_estuary_dark::*; +pub use atelier_estuary_light::*; +pub use atelier_forest_dark::*; +pub use atelier_forest_light::*; +pub use atelier_heath_dark::*; +pub use atelier_heath_light::*; +pub use atelier_lakeside_dark::*; +pub use atelier_lakeside_light::*; +pub use atelier_plateau_dark::*; +pub use atelier_plateau_light::*; +pub use atelier_savanna_dark::*; +pub use atelier_savanna_light::*; +pub use atelier_seaside_dark::*; +pub use atelier_seaside_light::*; +pub use atelier_sulphurpool_dark::*; +pub use atelier_sulphurpool_light::*; +pub use ayu_dark::*; +pub use ayu_light::*; +pub use ayu_mirage::*; +pub use gruvbox_dark::*; +pub use gruvbox_dark_hard::*; +pub use gruvbox_dark_soft::*; +pub use gruvbox_light::*; +pub use gruvbox_light_hard::*; +pub use gruvbox_light_soft::*; pub use one_dark::*; +pub use one_light::*; pub use rose_pine::*; +pub use rose_pine_dawn::*; +pub use rose_pine_moon::*; pub use sandcastle::*; +pub use solarized_dark::*; +pub use solarized_light::*; +pub use summercamp::*; diff --git a/crates/theme2/src/themes/one_dark.rs b/crates/theme2/src/themes/one_dark.rs index c59f4da16a..e81082a2d8 100644 --- a/crates/theme2/src/themes/one_dark.rs +++ b/crates/theme2/src/themes/one_dark.rs @@ -1,3 +1,4 @@ + use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; @@ -37,45 +38,45 @@ pub fn one_dark() -> Theme { icon_muted: rgba(0x838994ff).into(), syntax: SyntaxTheme { highlights: vec![ - ("link_uri".into(), rgba(0x6eb4bfff).into()), - ("number".into(), rgba(0xbf956aff).into()), - ("property".into(), rgba(0xd07277ff).into()), - ("boolean".into(), rgba(0xbf956aff).into()), - ("label".into(), rgba(0x74ade8ff).into()), - ("punctuation.list_marker".into(), rgba(0xd07277ff).into()), ("keyword".into(), rgba(0xb477cfff).into()), - ("punctuation.delimiter".into(), rgba(0xb2b9c6ff).into()), - ("string.special".into(), rgba(0xbf956aff).into()), - ("constant".into(), rgba(0xdfc184ff).into()), - ("punctuation".into(), rgba(0xacb2beff).into()), - ("variable.special".into(), rgba(0xbf956aff).into()), - ("preproc".into(), rgba(0xc8ccd4ff).into()), - ("enum".into(), rgba(0xd07277ff).into()), - ("attribute".into(), rgba(0x74ade8ff).into()), - ("emphasis.strong".into(), rgba(0xbf956aff).into()), - ("title".into(), rgba(0xd07277ff).into()), - ("hint".into(), rgba(0x5a6f89ff).into()), - ("emphasis".into(), rgba(0x74ade8ff).into()), - ("string.regex".into(), rgba(0xbf956aff).into()), - ("link_text".into(), rgba(0x73ade9ff).into()), - ("string".into(), rgba(0xa1c181ff).into()), ("comment.doc".into(), rgba(0x878e98ff).into()), - ("punctuation.special".into(), rgba(0xb1574bff).into()), - ("primary".into(), rgba(0xacb2beff).into()), - ("operator".into(), rgba(0x6eb4bfff).into()), - ("function".into(), rgba(0x73ade9ff).into()), - ("string.special.symbol".into(), rgba(0xbf956aff).into()), - ("type".into(), rgba(0x6eb4bfff).into()), ("variant".into(), rgba(0x73ade9ff).into()), + ("property".into(), rgba(0xd07277ff).into()), + ("function".into(), rgba(0x73ade9ff).into()), + ("type".into(), rgba(0x6eb4bfff).into()), ("tag".into(), rgba(0x74ade8ff).into()), - ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), - ("embedded".into(), rgba(0xc8ccd4ff).into()), ("string.escape".into(), rgba(0x878e98ff).into()), - ("variable".into(), rgba(0xc8ccd4ff).into()), - ("predictive".into(), rgba(0x5a6a87ff).into()), + ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), + ("hint".into(), rgba(0x5a6f89ff).into()), + ("punctuation".into(), rgba(0xacb2beff).into()), ("comment".into(), rgba(0x5d636fff).into()), - ("text.literal".into(), rgba(0xa1c181ff).into()), + ("emphasis".into(), rgba(0x74ade8ff).into()), + ("punctuation.special".into(), rgba(0xb1574bff).into()), + ("link_uri".into(), rgba(0x6eb4bfff).into()), + ("string.regex".into(), rgba(0xbf956aff).into()), ("constructor".into(), rgba(0x73ade9ff).into()), + ("operator".into(), rgba(0x6eb4bfff).into()), + ("constant".into(), rgba(0xdfc184ff).into()), + ("string.special".into(), rgba(0xbf956aff).into()), + ("emphasis.strong".into(), rgba(0xbf956aff).into()), + ("string.special.symbol".into(), rgba(0xbf956aff).into()), + ("primary".into(), rgba(0xacb2beff).into()), + ("preproc".into(), rgba(0xc8ccd4ff).into()), + ("string".into(), rgba(0xa1c181ff).into()), + ("punctuation.delimiter".into(), rgba(0xb2b9c6ff).into()), + ("embedded".into(), rgba(0xc8ccd4ff).into()), + ("enum".into(), rgba(0xd07277ff).into()), + ("variable.special".into(), rgba(0xbf956aff).into()), + ("text.literal".into(), rgba(0xa1c181ff).into()), + ("attribute".into(), rgba(0x74ade8ff).into()), + ("link_text".into(), rgba(0x73ade9ff).into()), + ("title".into(), rgba(0xd07277ff).into()), + ("predictive".into(), rgba(0x5a6a87ff).into()), + ("number".into(), rgba(0xbf956aff).into()), + ("label".into(), rgba(0x74ade8ff).into()), + ("variable".into(), rgba(0xc8ccd4ff).into()), + ("boolean".into(), rgba(0xbf956aff).into()), + ("punctuation.list_marker".into(), rgba(0xd07277ff).into()), ], }, status_bar: rgba(0x3b414dff).into(), diff --git a/crates/theme2/src/themes/one_light.rs b/crates/theme2/src/themes/one_light.rs new file mode 100644 index 0000000000..05528d6a55 --- /dev/null +++ b/crates/theme2/src/themes/one_light.rs @@ -0,0 +1,132 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn one_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "One Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xc9c9caff).into(), + border_variant: rgba(0xc9c9caff).into(), + border_focused: rgba(0xcbcdf6ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xdcdcddff).into(), + surface: rgba(0xebebecff).into(), + background: rgba(0xdcdcddff).into(), + filled_element: rgba(0xdcdcddff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xe2e2faff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xe2e2faff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x383a41ff).into(), + text_muted: rgba(0x7e8087ff).into(), + text_placeholder: rgba(0xd36151ff).into(), + text_disabled: rgba(0xa1a1a3ff).into(), + text_accent: rgba(0x5c78e2ff).into(), + icon_muted: rgba(0x7e8087ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.special.symbol".into(), rgba(0xad6e26ff).into()), + ("hint".into(), rgba(0x9294beff).into()), + ("link_uri".into(), rgba(0x3882b7ff).into()), + ("type".into(), rgba(0x3882b7ff).into()), + ("string.regex".into(), rgba(0xad6e26ff).into()), + ("constant".into(), rgba(0x669f59ff).into()), + ("function".into(), rgba(0x5b79e3ff).into()), + ("string.special".into(), rgba(0xad6e26ff).into()), + ("punctuation.bracket".into(), rgba(0x4d4f52ff).into()), + ("variable".into(), rgba(0x383a41ff).into()), + ("punctuation".into(), rgba(0x383a41ff).into()), + ("property".into(), rgba(0xd3604fff).into()), + ("string".into(), rgba(0x649f57ff).into()), + ("predictive".into(), rgba(0x9b9ec6ff).into()), + ("attribute".into(), rgba(0x5c78e2ff).into()), + ("number".into(), rgba(0xad6e25ff).into()), + ("constructor".into(), rgba(0x5c78e2ff).into()), + ("embedded".into(), rgba(0x383a41ff).into()), + ("title".into(), rgba(0xd3604fff).into()), + ("tag".into(), rgba(0x5c78e2ff).into()), + ("boolean".into(), rgba(0xad6e25ff).into()), + ("punctuation.list_marker".into(), rgba(0xd3604fff).into()), + ("variant".into(), rgba(0x5b79e3ff).into()), + ("emphasis".into(), rgba(0x5c78e2ff).into()), + ("link_text".into(), rgba(0x5b79e3ff).into()), + ("comment".into(), rgba(0xa2a3a7ff).into()), + ("punctuation.special".into(), rgba(0xb92b46ff).into()), + ("emphasis.strong".into(), rgba(0xad6e25ff).into()), + ("primary".into(), rgba(0x383a41ff).into()), + ("punctuation.delimiter".into(), rgba(0x4d4f52ff).into()), + ("label".into(), rgba(0x5c78e2ff).into()), + ("keyword".into(), rgba(0xa449abff).into()), + ("string.escape".into(), rgba(0x7c7e86ff).into()), + ("text.literal".into(), rgba(0x649f57ff).into()), + ("variable.special".into(), rgba(0xad6e25ff).into()), + ("comment.doc".into(), rgba(0x7c7e86ff).into()), + ("enum".into(), rgba(0xd3604fff).into()), + ("operator".into(), rgba(0x3882b7ff).into()), + ("preproc".into(), rgba(0x383a41ff).into()), + ], + }, + status_bar: rgba(0xdcdcddff).into(), + title_bar: rgba(0xdcdcddff).into(), + toolbar: rgba(0xfafafaff).into(), + tab_bar: rgba(0xebebecff).into(), + editor: rgba(0xfafafaff).into(), + editor_subheader: rgba(0xebebecff).into(), + editor_active_line: rgba(0xebebecff).into(), + terminal: rgba(0xfafafaff).into(), + image_fallback_background: rgba(0xdcdcddff).into(), + git_created: rgba(0x669f59ff).into(), + git_modified: rgba(0x5c78e2ff).into(), + git_deleted: rgba(0xd36151ff).into(), + git_conflict: rgba(0xdec184ff).into(), + git_ignored: rgba(0xa1a1a3ff).into(), + git_renamed: rgba(0xdec184ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x5c78e2ff).into(), + selection: rgba(0x5c78e23d).into(), + }, + PlayerTheme { + cursor: rgba(0x669f59ff).into(), + selection: rgba(0x669f593d).into(), + }, + PlayerTheme { + cursor: rgba(0x984ea5ff).into(), + selection: rgba(0x984ea53d).into(), + }, + PlayerTheme { + cursor: rgba(0xad6e26ff).into(), + selection: rgba(0xad6e263d).into(), + }, + PlayerTheme { + cursor: rgba(0xa349abff).into(), + selection: rgba(0xa349ab3d).into(), + }, + PlayerTheme { + cursor: rgba(0x3a82b7ff).into(), + selection: rgba(0x3a82b73d).into(), + }, + PlayerTheme { + cursor: rgba(0xd36151ff).into(), + selection: rgba(0xd361513d).into(), + }, + PlayerTheme { + cursor: rgba(0xdec184ff).into(), + selection: rgba(0xdec1843d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/rose_pine.rs b/crates/theme2/src/themes/rose_pine.rs index 674422c408..cbe88144ed 100644 --- a/crates/theme2/src/themes/rose_pine.rs +++ b/crates/theme2/src/themes/rose_pine.rs @@ -1,3 +1,4 @@ + use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; @@ -37,46 +38,46 @@ pub fn rose_pine() -> Theme { icon_muted: rgba(0x74708dff).into(), syntax: SyntaxTheme { highlights: vec![ - ("text.literal".into(), rgba(0xc4a7e6ff).into()), - ("string".into(), rgba(0xf5c177ff).into()), - ("enum".into(), rgba(0xc4a7e6ff).into()), - ("number".into(), rgba(0x5cc1a3ff).into()), - ("attribute".into(), rgba(0x9bced6ff).into()), - ("property".into(), rgba(0x9bced6ff).into()), - ("function".into(), rgba(0xebbcbaff).into()), - ("embedded".into(), rgba(0xe0def4ff).into()), ("punctuation.delimiter".into(), rgba(0x9d99b6ff).into()), - ("variant".into(), rgba(0x9bced6ff).into()), - ("operator".into(), rgba(0x30738fff).into()), - ("comment".into(), rgba(0x6e6a86ff).into()), - ("type.builtin".into(), rgba(0x9ccfd8ff).into()), - ("label".into(), rgba(0x9bced6ff).into()), - ("string.escape".into(), rgba(0x76728fff).into()), - ("type".into(), rgba(0x9ccfd8ff).into()), - ("constructor".into(), rgba(0x9bced6ff).into()), - ("punctuation.bracket".into(), rgba(0x9d99b6ff).into()), - ("function.method".into(), rgba(0xebbcbaff).into()), - ("tag".into(), rgba(0x9ccfd8ff).into()), - ("link_text".into(), rgba(0x9ccfd8ff).into()), - ("string.special".into(), rgba(0xc4a7e6ff).into()), - ("string.regex".into(), rgba(0xc4a7e6ff).into()), - ("preproc".into(), rgba(0xe0def4ff).into()), - ("emphasis.strong".into(), rgba(0x9bced6ff).into()), - ("emphasis".into(), rgba(0x9bced6ff).into()), - ("comment.doc".into(), rgba(0x76728fff).into()), - ("boolean".into(), rgba(0xebbcbaff).into()), - ("punctuation.list_marker".into(), rgba(0x9d99b6ff).into()), - ("hint".into(), rgba(0x5e768cff).into()), - ("title".into(), rgba(0xf5c177ff).into()), - ("variable".into(), rgba(0xe0def4ff).into()), - ("string.special.symbol".into(), rgba(0xc4a7e6ff).into()), - ("primary".into(), rgba(0xe0def4ff).into()), - ("predictive".into(), rgba(0x556b81ff).into()), - ("punctuation".into(), rgba(0x908caaff).into()), - ("constant".into(), rgba(0x5cc1a3ff).into()), + ("number".into(), rgba(0x5cc1a3ff).into()), ("punctuation.special".into(), rgba(0x9d99b6ff).into()), + ("string.escape".into(), rgba(0x76728fff).into()), + ("title".into(), rgba(0xf5c177ff).into()), + ("constant".into(), rgba(0x5cc1a3ff).into()), + ("string.regex".into(), rgba(0xc4a7e6ff).into()), + ("type.builtin".into(), rgba(0x9ccfd8ff).into()), + ("comment.doc".into(), rgba(0x76728fff).into()), + ("primary".into(), rgba(0xe0def4ff).into()), + ("string.special".into(), rgba(0xc4a7e6ff).into()), + ("punctuation".into(), rgba(0x908caaff).into()), + ("string.special.symbol".into(), rgba(0xc4a7e6ff).into()), + ("variant".into(), rgba(0x9bced6ff).into()), + ("function.method".into(), rgba(0xebbcbaff).into()), + ("comment".into(), rgba(0x6e6a86ff).into()), + ("boolean".into(), rgba(0xebbcbaff).into()), + ("preproc".into(), rgba(0xe0def4ff).into()), ("link_uri".into(), rgba(0xebbcbaff).into()), + ("hint".into(), rgba(0x5e768cff).into()), + ("attribute".into(), rgba(0x9bced6ff).into()), + ("text.literal".into(), rgba(0xc4a7e6ff).into()), + ("punctuation.list_marker".into(), rgba(0x9d99b6ff).into()), + ("operator".into(), rgba(0x30738fff).into()), + ("emphasis.strong".into(), rgba(0x9bced6ff).into()), ("keyword".into(), rgba(0x30738fff).into()), + ("enum".into(), rgba(0xc4a7e6ff).into()), + ("tag".into(), rgba(0x9ccfd8ff).into()), + ("constructor".into(), rgba(0x9bced6ff).into()), + ("function".into(), rgba(0xebbcbaff).into()), + ("string".into(), rgba(0xf5c177ff).into()), + ("type".into(), rgba(0x9ccfd8ff).into()), + ("emphasis".into(), rgba(0x9bced6ff).into()), + ("link_text".into(), rgba(0x9ccfd8ff).into()), + ("property".into(), rgba(0x9bced6ff).into()), + ("predictive".into(), rgba(0x556b81ff).into()), + ("punctuation.bracket".into(), rgba(0x9d99b6ff).into()), + ("embedded".into(), rgba(0xe0def4ff).into()), + ("variable".into(), rgba(0xe0def4ff).into()), + ("label".into(), rgba(0x9bced6ff).into()), ], }, status_bar: rgba(0x292738ff).into(), @@ -130,261 +131,3 @@ pub fn rose_pine() -> Theme { ], } } - -pub fn rose_pine_dawn() -> Theme { - Theme { - metadata: ThemeMetadata { - name: "Rosé Pine Dawn".into(), - is_light: true, - }, - transparent: rgba(0x00000000).into(), - mac_os_traffic_light_red: rgba(0xec695eff).into(), - mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), - mac_os_traffic_light_green: rgba(0x61c553ff).into(), - border: rgba(0xdcd6d5ff).into(), - border_variant: rgba(0xdcd6d5ff).into(), - border_focused: rgba(0xc3d7dbff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0xdcd8d8ff).into(), - surface: rgba(0xfef9f2ff).into(), - background: rgba(0xdcd8d8ff).into(), - filled_element: rgba(0xdcd8d8ff).into(), - filled_element_hover: rgba(0xffffff1e).into(), - filled_element_active: rgba(0xffffff28).into(), - filled_element_selected: rgba(0xdde9ebff).into(), - filled_element_disabled: rgba(0x00000000).into(), - ghost_element: rgba(0x00000000).into(), - ghost_element_hover: rgba(0xffffff14).into(), - ghost_element_active: rgba(0xffffff1e).into(), - ghost_element_selected: rgba(0xdde9ebff).into(), - ghost_element_disabled: rgba(0x00000000).into(), - text: rgba(0x575279ff).into(), - text_muted: rgba(0x706c8cff).into(), - text_placeholder: rgba(0xb4647aff).into(), - text_disabled: rgba(0x938fa3ff).into(), - text_accent: rgba(0x57949fff).into(), - icon_muted: rgba(0x706c8cff).into(), - syntax: SyntaxTheme { - highlights: vec![ - ("type".into(), rgba(0x55949fff).into()), - ("keyword".into(), rgba(0x276983ff).into()), - ("link_text".into(), rgba(0x55949fff).into()), - ("embedded".into(), rgba(0x575279ff).into()), - ("type.builtin".into(), rgba(0x55949fff).into()), - ("punctuation.delimiter".into(), rgba(0x635e82ff).into()), - ("text.literal".into(), rgba(0x9079a9ff).into()), - ("variant".into(), rgba(0x57949fff).into()), - ("string".into(), rgba(0xea9d34ff).into()), - ("hint".into(), rgba(0x7a92aaff).into()), - ("punctuation.special".into(), rgba(0x635e82ff).into()), - ("string.special".into(), rgba(0x9079a9ff).into()), - ("string.regex".into(), rgba(0x9079a9ff).into()), - ("operator".into(), rgba(0x276983ff).into()), - ("boolean".into(), rgba(0xd7827dff).into()), - ("constructor".into(), rgba(0x57949fff).into()), - ("punctuation".into(), rgba(0x797593ff).into()), - ("label".into(), rgba(0x57949fff).into()), - ("variable".into(), rgba(0x575279ff).into()), - ("tag".into(), rgba(0x55949fff).into()), - ("primary".into(), rgba(0x575279ff).into()), - ("link_uri".into(), rgba(0xd7827dff).into()), - ("punctuation.list_marker".into(), rgba(0x635e82ff).into()), - ("string.escape".into(), rgba(0x6e6a8bff).into()), - ("punctuation.bracket".into(), rgba(0x635e82ff).into()), - ("function".into(), rgba(0xd7827dff).into()), - ("preproc".into(), rgba(0x575279ff).into()), - ("function.method".into(), rgba(0xd7827dff).into()), - ("predictive".into(), rgba(0xa2acbeff).into()), - ("comment.doc".into(), rgba(0x6e6a8bff).into()), - ("comment".into(), rgba(0x9893a5ff).into()), - ("number".into(), rgba(0x3daa8eff).into()), - ("emphasis".into(), rgba(0x57949fff).into()), - ("title".into(), rgba(0xea9d34ff).into()), - ("enum".into(), rgba(0x9079a9ff).into()), - ("string.special.symbol".into(), rgba(0x9079a9ff).into()), - ("constant".into(), rgba(0x3daa8eff).into()), - ("emphasis.strong".into(), rgba(0x57949fff).into()), - ("property".into(), rgba(0x57949fff).into()), - ("attribute".into(), rgba(0x57949fff).into()), - ], - }, - status_bar: rgba(0xdcd8d8ff).into(), - title_bar: rgba(0xdcd8d8ff).into(), - toolbar: rgba(0xfaf4edff).into(), - tab_bar: rgba(0xfef9f2ff).into(), - editor: rgba(0xfaf4edff).into(), - editor_subheader: rgba(0xfef9f2ff).into(), - editor_active_line: rgba(0xfef9f2ff).into(), - terminal: rgba(0xfaf4edff).into(), - image_fallback_background: rgba(0xdcd8d8ff).into(), - git_created: rgba(0x3daa8eff).into(), - git_modified: rgba(0x57949fff).into(), - git_deleted: rgba(0xb4647aff).into(), - git_conflict: rgba(0xe99d35ff).into(), - git_ignored: rgba(0x938fa3ff).into(), - git_renamed: rgba(0xe99d35ff).into(), - players: [ - PlayerTheme { - cursor: rgba(0x57949fff).into(), - selection: rgba(0x57949f3d).into(), - }, - PlayerTheme { - cursor: rgba(0x3daa8eff).into(), - selection: rgba(0x3daa8e3d).into(), - }, - PlayerTheme { - cursor: rgba(0x7c697fff).into(), - selection: rgba(0x7c697f3d).into(), - }, - PlayerTheme { - cursor: rgba(0x9079a9ff).into(), - selection: rgba(0x9079a93d).into(), - }, - PlayerTheme { - cursor: rgba(0x9079a9ff).into(), - selection: rgba(0x9079a93d).into(), - }, - PlayerTheme { - cursor: rgba(0x296983ff).into(), - selection: rgba(0x2969833d).into(), - }, - PlayerTheme { - cursor: rgba(0xb4647aff).into(), - selection: rgba(0xb4647a3d).into(), - }, - PlayerTheme { - cursor: rgba(0xe99d35ff).into(), - selection: rgba(0xe99d353d).into(), - }, - ], - } -} - -pub fn rose_pine_moon() -> Theme { - Theme { - metadata: ThemeMetadata { - name: "Rosé Pine Moon".into(), - is_light: false, - }, - transparent: rgba(0x00000000).into(), - mac_os_traffic_light_red: rgba(0xec695eff).into(), - mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), - mac_os_traffic_light_green: rgba(0x61c553ff).into(), - border: rgba(0x504c68ff).into(), - border_variant: rgba(0x504c68ff).into(), - border_focused: rgba(0x435255ff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0x38354eff).into(), - surface: rgba(0x28253cff).into(), - background: rgba(0x38354eff).into(), - filled_element: rgba(0x38354eff).into(), - filled_element_hover: rgba(0xffffff1e).into(), - filled_element_active: rgba(0xffffff28).into(), - filled_element_selected: rgba(0x2f3639ff).into(), - filled_element_disabled: rgba(0x00000000).into(), - ghost_element: rgba(0x00000000).into(), - ghost_element_hover: rgba(0xffffff14).into(), - ghost_element_active: rgba(0xffffff1e).into(), - ghost_element_selected: rgba(0x2f3639ff).into(), - ghost_element_disabled: rgba(0x00000000).into(), - text: rgba(0xe0def4ff).into(), - text_muted: rgba(0x85819eff).into(), - text_placeholder: rgba(0xea6e92ff).into(), - text_disabled: rgba(0x605d7aff).into(), - text_accent: rgba(0x9bced6ff).into(), - icon_muted: rgba(0x85819eff).into(), - syntax: SyntaxTheme { - highlights: vec![ - ("embedded".into(), rgba(0xe0def4ff).into()), - ("link_uri".into(), rgba(0xea9a97ff).into()), - ("primary".into(), rgba(0xe0def4ff).into()), - ("punctuation.delimiter".into(), rgba(0xaeabc6ff).into()), - ("string.escape".into(), rgba(0x8682a0ff).into()), - ("attribute".into(), rgba(0x9bced6ff).into()), - ("constant".into(), rgba(0x5cc1a3ff).into()), - ("keyword".into(), rgba(0x3d8fb0ff).into()), - ("predictive".into(), rgba(0x516b83ff).into()), - ("label".into(), rgba(0x9bced6ff).into()), - ("comment.doc".into(), rgba(0x8682a0ff).into()), - ("emphasis".into(), rgba(0x9bced6ff).into()), - ("string".into(), rgba(0xf5c177ff).into()), - ("type".into(), rgba(0x9ccfd8ff).into()), - ("string.special".into(), rgba(0xc4a7e6ff).into()), - ("function".into(), rgba(0xea9a97ff).into()), - ("constructor".into(), rgba(0x9bced6ff).into()), - ("comment".into(), rgba(0x6e6a86ff).into()), - ("preproc".into(), rgba(0xe0def4ff).into()), - ("enum".into(), rgba(0xc4a7e6ff).into()), - ("punctuation.bracket".into(), rgba(0xaeabc6ff).into()), - ("number".into(), rgba(0x5cc1a3ff).into()), - ("hint".into(), rgba(0x728aa2ff).into()), - ("variant".into(), rgba(0x9bced6ff).into()), - ("link_text".into(), rgba(0x9ccfd8ff).into()), - ("property".into(), rgba(0x9bced6ff).into()), - ("punctuation.list_marker".into(), rgba(0xaeabc6ff).into()), - ("operator".into(), rgba(0x3d8fb0ff).into()), - ("title".into(), rgba(0xf5c177ff).into()), - ("punctuation".into(), rgba(0x908caaff).into()), - ("string.regex".into(), rgba(0xc4a7e6ff).into()), - ("tag".into(), rgba(0x9ccfd8ff).into()), - ("emphasis.strong".into(), rgba(0x9bced6ff).into()), - ("text.literal".into(), rgba(0xc4a7e6ff).into()), - ("punctuation.special".into(), rgba(0xaeabc6ff).into()), - ("boolean".into(), rgba(0xea9a97ff).into()), - ("type.builtin".into(), rgba(0x9ccfd8ff).into()), - ("function.method".into(), rgba(0xea9a97ff).into()), - ("variable".into(), rgba(0xe0def4ff).into()), - ("string.special.symbol".into(), rgba(0xc4a7e6ff).into()), - ], - }, - status_bar: rgba(0x38354eff).into(), - title_bar: rgba(0x38354eff).into(), - toolbar: rgba(0x232136ff).into(), - tab_bar: rgba(0x28253cff).into(), - editor: rgba(0x232136ff).into(), - editor_subheader: rgba(0x28253cff).into(), - editor_active_line: rgba(0x28253cff).into(), - terminal: rgba(0x232136ff).into(), - image_fallback_background: rgba(0x38354eff).into(), - git_created: rgba(0x5cc1a3ff).into(), - git_modified: rgba(0x9bced6ff).into(), - git_deleted: rgba(0xea6e92ff).into(), - git_conflict: rgba(0xf5c177ff).into(), - git_ignored: rgba(0x605d7aff).into(), - git_renamed: rgba(0xf5c177ff).into(), - players: [ - PlayerTheme { - cursor: rgba(0x9bced6ff).into(), - selection: rgba(0x9bced63d).into(), - }, - PlayerTheme { - cursor: rgba(0x5cc1a3ff).into(), - selection: rgba(0x5cc1a33d).into(), - }, - PlayerTheme { - cursor: rgba(0xa683a0ff).into(), - selection: rgba(0xa683a03d).into(), - }, - PlayerTheme { - cursor: rgba(0xc4a7e6ff).into(), - selection: rgba(0xc4a7e63d).into(), - }, - PlayerTheme { - cursor: rgba(0xc4a7e6ff).into(), - selection: rgba(0xc4a7e63d).into(), - }, - PlayerTheme { - cursor: rgba(0x3e8fb0ff).into(), - selection: rgba(0x3e8fb03d).into(), - }, - PlayerTheme { - cursor: rgba(0xea6e92ff).into(), - selection: rgba(0xea6e923d).into(), - }, - PlayerTheme { - cursor: rgba(0xf5c177ff).into(), - selection: rgba(0xf5c1773d).into(), - }, - ], - } -} diff --git a/crates/theme2/src/themes/rose_pine_dawn.rs b/crates/theme2/src/themes/rose_pine_dawn.rs new file mode 100644 index 0000000000..66dde6730f --- /dev/null +++ b/crates/theme2/src/themes/rose_pine_dawn.rs @@ -0,0 +1,133 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn rose_pine_dawn() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Rosé Pine Dawn".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0xdcd6d5ff).into(), + border_variant: rgba(0xdcd6d5ff).into(), + border_focused: rgba(0xc3d7dbff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xdcd8d8ff).into(), + surface: rgba(0xfef9f2ff).into(), + background: rgba(0xdcd8d8ff).into(), + filled_element: rgba(0xdcd8d8ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xdde9ebff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xdde9ebff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x575279ff).into(), + text_muted: rgba(0x706c8cff).into(), + text_placeholder: rgba(0xb4647aff).into(), + text_disabled: rgba(0x938fa3ff).into(), + text_accent: rgba(0x57949fff).into(), + icon_muted: rgba(0x706c8cff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("primary".into(), rgba(0x575279ff).into()), + ("attribute".into(), rgba(0x57949fff).into()), + ("operator".into(), rgba(0x276983ff).into()), + ("boolean".into(), rgba(0xd7827dff).into()), + ("tag".into(), rgba(0x55949fff).into()), + ("enum".into(), rgba(0x9079a9ff).into()), + ("embedded".into(), rgba(0x575279ff).into()), + ("label".into(), rgba(0x57949fff).into()), + ("function.method".into(), rgba(0xd7827dff).into()), + ("punctuation.list_marker".into(), rgba(0x635e82ff).into()), + ("punctuation.delimiter".into(), rgba(0x635e82ff).into()), + ("string".into(), rgba(0xea9d34ff).into()), + ("type".into(), rgba(0x55949fff).into()), + ("string.regex".into(), rgba(0x9079a9ff).into()), + ("variable".into(), rgba(0x575279ff).into()), + ("constructor".into(), rgba(0x57949fff).into()), + ("punctuation.bracket".into(), rgba(0x635e82ff).into()), + ("emphasis".into(), rgba(0x57949fff).into()), + ("comment.doc".into(), rgba(0x6e6a8bff).into()), + ("comment".into(), rgba(0x9893a5ff).into()), + ("keyword".into(), rgba(0x276983ff).into()), + ("preproc".into(), rgba(0x575279ff).into()), + ("string.special".into(), rgba(0x9079a9ff).into()), + ("string.escape".into(), rgba(0x6e6a8bff).into()), + ("constant".into(), rgba(0x3daa8eff).into()), + ("property".into(), rgba(0x57949fff).into()), + ("punctuation.special".into(), rgba(0x635e82ff).into()), + ("text.literal".into(), rgba(0x9079a9ff).into()), + ("type.builtin".into(), rgba(0x55949fff).into()), + ("string.special.symbol".into(), rgba(0x9079a9ff).into()), + ("link_uri".into(), rgba(0xd7827dff).into()), + ("number".into(), rgba(0x3daa8eff).into()), + ("emphasis.strong".into(), rgba(0x57949fff).into()), + ("function".into(), rgba(0xd7827dff).into()), + ("title".into(), rgba(0xea9d34ff).into()), + ("punctuation".into(), rgba(0x797593ff).into()), + ("link_text".into(), rgba(0x55949fff).into()), + ("variant".into(), rgba(0x57949fff).into()), + ("predictive".into(), rgba(0xa2acbeff).into()), + ("hint".into(), rgba(0x7a92aaff).into()), + ], + }, + status_bar: rgba(0xdcd8d8ff).into(), + title_bar: rgba(0xdcd8d8ff).into(), + toolbar: rgba(0xfaf4edff).into(), + tab_bar: rgba(0xfef9f2ff).into(), + editor: rgba(0xfaf4edff).into(), + editor_subheader: rgba(0xfef9f2ff).into(), + editor_active_line: rgba(0xfef9f2ff).into(), + terminal: rgba(0xfaf4edff).into(), + image_fallback_background: rgba(0xdcd8d8ff).into(), + git_created: rgba(0x3daa8eff).into(), + git_modified: rgba(0x57949fff).into(), + git_deleted: rgba(0xb4647aff).into(), + git_conflict: rgba(0xe99d35ff).into(), + git_ignored: rgba(0x938fa3ff).into(), + git_renamed: rgba(0xe99d35ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x57949fff).into(), + selection: rgba(0x57949f3d).into(), + }, + PlayerTheme { + cursor: rgba(0x3daa8eff).into(), + selection: rgba(0x3daa8e3d).into(), + }, + PlayerTheme { + cursor: rgba(0x7c697fff).into(), + selection: rgba(0x7c697f3d).into(), + }, + PlayerTheme { + cursor: rgba(0x9079a9ff).into(), + selection: rgba(0x9079a93d).into(), + }, + PlayerTheme { + cursor: rgba(0x9079a9ff).into(), + selection: rgba(0x9079a93d).into(), + }, + PlayerTheme { + cursor: rgba(0x296983ff).into(), + selection: rgba(0x2969833d).into(), + }, + PlayerTheme { + cursor: rgba(0xb4647aff).into(), + selection: rgba(0xb4647a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xe99d35ff).into(), + selection: rgba(0xe99d353d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/rose_pine_moon.rs b/crates/theme2/src/themes/rose_pine_moon.rs new file mode 100644 index 0000000000..ce96003705 --- /dev/null +++ b/crates/theme2/src/themes/rose_pine_moon.rs @@ -0,0 +1,133 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn rose_pine_moon() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Rosé Pine Moon".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x504c68ff).into(), + border_variant: rgba(0x504c68ff).into(), + border_focused: rgba(0x435255ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x38354eff).into(), + surface: rgba(0x28253cff).into(), + background: rgba(0x38354eff).into(), + filled_element: rgba(0x38354eff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x2f3639ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x2f3639ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xe0def4ff).into(), + text_muted: rgba(0x85819eff).into(), + text_placeholder: rgba(0xea6e92ff).into(), + text_disabled: rgba(0x605d7aff).into(), + text_accent: rgba(0x9bced6ff).into(), + icon_muted: rgba(0x85819eff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("type.builtin".into(), rgba(0x9ccfd8ff).into()), + ("variable".into(), rgba(0xe0def4ff).into()), + ("punctuation".into(), rgba(0x908caaff).into()), + ("number".into(), rgba(0x5cc1a3ff).into()), + ("comment".into(), rgba(0x6e6a86ff).into()), + ("string.special".into(), rgba(0xc4a7e6ff).into()), + ("string.escape".into(), rgba(0x8682a0ff).into()), + ("function.method".into(), rgba(0xea9a97ff).into()), + ("predictive".into(), rgba(0x516b83ff).into()), + ("punctuation.delimiter".into(), rgba(0xaeabc6ff).into()), + ("primary".into(), rgba(0xe0def4ff).into()), + ("link_text".into(), rgba(0x9ccfd8ff).into()), + ("string.regex".into(), rgba(0xc4a7e6ff).into()), + ("constructor".into(), rgba(0x9bced6ff).into()), + ("constant".into(), rgba(0x5cc1a3ff).into()), + ("emphasis.strong".into(), rgba(0x9bced6ff).into()), + ("function".into(), rgba(0xea9a97ff).into()), + ("hint".into(), rgba(0x728aa2ff).into()), + ("preproc".into(), rgba(0xe0def4ff).into()), + ("property".into(), rgba(0x9bced6ff).into()), + ("punctuation.list_marker".into(), rgba(0xaeabc6ff).into()), + ("emphasis".into(), rgba(0x9bced6ff).into()), + ("attribute".into(), rgba(0x9bced6ff).into()), + ("title".into(), rgba(0xf5c177ff).into()), + ("keyword".into(), rgba(0x3d8fb0ff).into()), + ("string".into(), rgba(0xf5c177ff).into()), + ("text.literal".into(), rgba(0xc4a7e6ff).into()), + ("embedded".into(), rgba(0xe0def4ff).into()), + ("comment.doc".into(), rgba(0x8682a0ff).into()), + ("variant".into(), rgba(0x9bced6ff).into()), + ("label".into(), rgba(0x9bced6ff).into()), + ("punctuation.special".into(), rgba(0xaeabc6ff).into()), + ("string.special.symbol".into(), rgba(0xc4a7e6ff).into()), + ("tag".into(), rgba(0x9ccfd8ff).into()), + ("enum".into(), rgba(0xc4a7e6ff).into()), + ("boolean".into(), rgba(0xea9a97ff).into()), + ("punctuation.bracket".into(), rgba(0xaeabc6ff).into()), + ("operator".into(), rgba(0x3d8fb0ff).into()), + ("type".into(), rgba(0x9ccfd8ff).into()), + ("link_uri".into(), rgba(0xea9a97ff).into()), + ], + }, + status_bar: rgba(0x38354eff).into(), + title_bar: rgba(0x38354eff).into(), + toolbar: rgba(0x232136ff).into(), + tab_bar: rgba(0x28253cff).into(), + editor: rgba(0x232136ff).into(), + editor_subheader: rgba(0x28253cff).into(), + editor_active_line: rgba(0x28253cff).into(), + terminal: rgba(0x232136ff).into(), + image_fallback_background: rgba(0x38354eff).into(), + git_created: rgba(0x5cc1a3ff).into(), + git_modified: rgba(0x9bced6ff).into(), + git_deleted: rgba(0xea6e92ff).into(), + git_conflict: rgba(0xf5c177ff).into(), + git_ignored: rgba(0x605d7aff).into(), + git_renamed: rgba(0xf5c177ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x9bced6ff).into(), + selection: rgba(0x9bced63d).into(), + }, + PlayerTheme { + cursor: rgba(0x5cc1a3ff).into(), + selection: rgba(0x5cc1a33d).into(), + }, + PlayerTheme { + cursor: rgba(0xa683a0ff).into(), + selection: rgba(0xa683a03d).into(), + }, + PlayerTheme { + cursor: rgba(0xc4a7e6ff).into(), + selection: rgba(0xc4a7e63d).into(), + }, + PlayerTheme { + cursor: rgba(0xc4a7e6ff).into(), + selection: rgba(0xc4a7e63d).into(), + }, + PlayerTheme { + cursor: rgba(0x3e8fb0ff).into(), + selection: rgba(0x3e8fb03d).into(), + }, + PlayerTheme { + cursor: rgba(0xea6e92ff).into(), + selection: rgba(0xea6e923d).into(), + }, + PlayerTheme { + cursor: rgba(0xf5c177ff).into(), + selection: rgba(0xf5c1773d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/sandcastle.rs b/crates/theme2/src/themes/sandcastle.rs index 4e87c427f0..2004033239 100644 --- a/crates/theme2/src/themes/sandcastle.rs +++ b/crates/theme2/src/themes/sandcastle.rs @@ -1,3 +1,4 @@ + use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; @@ -37,44 +38,44 @@ pub fn sandcastle() -> Theme { icon_muted: rgba(0xa69782ff).into(), syntax: SyntaxTheme { highlights: vec![ - ("string.special.symbol".into(), rgba(0xa07d3aff).into()), - ("enum".into(), rgba(0xa07d3aff).into()), + ("comment".into(), rgba(0xa89984ff).into()), + ("type".into(), rgba(0x83a598ff).into()), + ("preproc".into(), rgba(0xfdf4c1ff).into()), ("punctuation.bracket".into(), rgba(0xd5c5a1ff).into()), ("hint".into(), rgba(0x727d68ff).into()), - ("punctuation.delimiter".into(), rgba(0xd5c5a1ff).into()), - ("comment".into(), rgba(0xa89984ff).into()), - ("embedded".into(), rgba(0xfdf4c1ff).into()), + ("link_uri".into(), rgba(0x83a598ff).into()), + ("text.literal".into(), rgba(0xa07d3aff).into()), + ("enum".into(), rgba(0xa07d3aff).into()), + ("string.special".into(), rgba(0xa07d3aff).into()), ("string".into(), rgba(0xa07d3aff).into()), - ("string.escape".into(), rgba(0xa89984ff).into()), - ("comment.doc".into(), rgba(0xa89984ff).into()), - ("variant".into(), rgba(0x518b8bff).into()), + ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), + ("keyword".into(), rgba(0x518b8bff).into()), + ("constructor".into(), rgba(0x518b8bff).into()), ("predictive".into(), rgba(0x5c6152ff).into()), - ("link_text".into(), rgba(0xa07d3aff).into()), - ("attribute".into(), rgba(0x518b8bff).into()), ("title".into(), rgba(0xfdf4c1ff).into()), + ("variable".into(), rgba(0xfdf4c1ff).into()), ("emphasis.strong".into(), rgba(0x518b8bff).into()), ("primary".into(), rgba(0xfdf4c1ff).into()), - ("punctuation.list_marker".into(), rgba(0xd5c5a1ff).into()), - ("boolean".into(), rgba(0x83a598ff).into()), - ("function".into(), rgba(0xa07d3aff).into()), - ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), - ("string.special".into(), rgba(0xa07d3aff).into()), - ("string.regex".into(), rgba(0xa07d3aff).into()), - ("tag".into(), rgba(0x518b8bff).into()), - ("keyword".into(), rgba(0x518b8bff).into()), - ("type".into(), rgba(0x83a598ff).into()), - ("text.literal".into(), rgba(0xa07d3aff).into()), - ("link_uri".into(), rgba(0x83a598ff).into()), - ("label".into(), rgba(0x518b8bff).into()), - ("property".into(), rgba(0x518b8bff).into()), - ("number".into(), rgba(0x83a598ff).into()), - ("constructor".into(), rgba(0x518b8bff).into()), - ("preproc".into(), rgba(0xfdf4c1ff).into()), ("emphasis".into(), rgba(0x518b8bff).into()), - ("variable".into(), rgba(0xfdf4c1ff).into()), - ("operator".into(), rgba(0xa07d3aff).into()), ("punctuation".into(), rgba(0xd5c5a1ff).into()), ("constant".into(), rgba(0x83a598ff).into()), + ("link_text".into(), rgba(0xa07d3aff).into()), + ("punctuation.delimiter".into(), rgba(0xd5c5a1ff).into()), + ("embedded".into(), rgba(0xfdf4c1ff).into()), + ("string.special.symbol".into(), rgba(0xa07d3aff).into()), + ("tag".into(), rgba(0x518b8bff).into()), + ("punctuation.list_marker".into(), rgba(0xd5c5a1ff).into()), + ("operator".into(), rgba(0xa07d3aff).into()), + ("boolean".into(), rgba(0x83a598ff).into()), + ("function".into(), rgba(0xa07d3aff).into()), + ("attribute".into(), rgba(0x518b8bff).into()), + ("number".into(), rgba(0x83a598ff).into()), + ("string.escape".into(), rgba(0xa89984ff).into()), + ("comment.doc".into(), rgba(0xa89984ff).into()), + ("label".into(), rgba(0x518b8bff).into()), + ("string.regex".into(), rgba(0xa07d3aff).into()), + ("property".into(), rgba(0x518b8bff).into()), + ("variant".into(), rgba(0x518b8bff).into()), ], }, status_bar: rgba(0x333944ff).into(), diff --git a/crates/theme2/src/themes/solarized_dark.rs b/crates/theme2/src/themes/solarized_dark.rs new file mode 100644 index 0000000000..1c58ef6008 --- /dev/null +++ b/crates/theme2/src/themes/solarized_dark.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn solarized_dark() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Solarized Dark".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x2b4e58ff).into(), + border_variant: rgba(0x2b4e58ff).into(), + border_focused: rgba(0x1b3149ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x073743ff).into(), + surface: rgba(0x04313bff).into(), + background: rgba(0x073743ff).into(), + filled_element: rgba(0x073743ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x141f2cff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x141f2cff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xfdf6e3ff).into(), + text_muted: rgba(0x93a1a1ff).into(), + text_placeholder: rgba(0xdc3330ff).into(), + text_disabled: rgba(0x6f8389ff).into(), + text_accent: rgba(0x278ad1ff).into(), + icon_muted: rgba(0x93a1a1ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("punctuation.special".into(), rgba(0xefe9d6ff).into()), + ("string".into(), rgba(0xcb4b16ff).into()), + ("variant".into(), rgba(0x278ad1ff).into()), + ("variable".into(), rgba(0xfdf6e3ff).into()), + ("string.special.symbol".into(), rgba(0xcb4b16ff).into()), + ("primary".into(), rgba(0xfdf6e3ff).into()), + ("type".into(), rgba(0x2ba198ff).into()), + ("boolean".into(), rgba(0x849903ff).into()), + ("string.special".into(), rgba(0xcb4b16ff).into()), + ("label".into(), rgba(0x278ad1ff).into()), + ("link_uri".into(), rgba(0x849903ff).into()), + ("constructor".into(), rgba(0x278ad1ff).into()), + ("hint".into(), rgba(0x4f8297ff).into()), + ("preproc".into(), rgba(0xfdf6e3ff).into()), + ("text.literal".into(), rgba(0xcb4b16ff).into()), + ("string.escape".into(), rgba(0x99a5a4ff).into()), + ("link_text".into(), rgba(0xcb4b16ff).into()), + ("comment".into(), rgba(0x99a5a4ff).into()), + ("enum".into(), rgba(0xcb4b16ff).into()), + ("constant".into(), rgba(0x849903ff).into()), + ("comment.doc".into(), rgba(0x99a5a4ff).into()), + ("emphasis".into(), rgba(0x278ad1ff).into()), + ("predictive".into(), rgba(0x3f718bff).into()), + ("attribute".into(), rgba(0x278ad1ff).into()), + ("punctuation.delimiter".into(), rgba(0xefe9d6ff).into()), + ("function".into(), rgba(0xb58902ff).into()), + ("emphasis.strong".into(), rgba(0x278ad1ff).into()), + ("tag".into(), rgba(0x278ad1ff).into()), + ("string.regex".into(), rgba(0xcb4b16ff).into()), + ("property".into(), rgba(0x278ad1ff).into()), + ("keyword".into(), rgba(0x278ad1ff).into()), + ("number".into(), rgba(0x849903ff).into()), + ("embedded".into(), rgba(0xfdf6e3ff).into()), + ("operator".into(), rgba(0xcb4b16ff).into()), + ("punctuation".into(), rgba(0xefe9d6ff).into()), + ("punctuation.bracket".into(), rgba(0xefe9d6ff).into()), + ("title".into(), rgba(0xfdf6e3ff).into()), + ("punctuation.list_marker".into(), rgba(0xefe9d6ff).into()), + ], + }, + status_bar: rgba(0x073743ff).into(), + title_bar: rgba(0x073743ff).into(), + toolbar: rgba(0x002a35ff).into(), + tab_bar: rgba(0x04313bff).into(), + editor: rgba(0x002a35ff).into(), + editor_subheader: rgba(0x04313bff).into(), + editor_active_line: rgba(0x04313bff).into(), + terminal: rgba(0x002a35ff).into(), + image_fallback_background: rgba(0x073743ff).into(), + git_created: rgba(0x849903ff).into(), + git_modified: rgba(0x278ad1ff).into(), + git_deleted: rgba(0xdc3330ff).into(), + git_conflict: rgba(0xb58902ff).into(), + git_ignored: rgba(0x6f8389ff).into(), + git_renamed: rgba(0xb58902ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x278ad1ff).into(), + selection: rgba(0x278ad13d).into(), + }, + PlayerTheme { + cursor: rgba(0x849903ff).into(), + selection: rgba(0x8499033d).into(), + }, + PlayerTheme { + cursor: rgba(0xd33781ff).into(), + selection: rgba(0xd337813d).into(), + }, + PlayerTheme { + cursor: rgba(0xcb4b16ff).into(), + selection: rgba(0xcb4b163d).into(), + }, + PlayerTheme { + cursor: rgba(0x6c71c4ff).into(), + selection: rgba(0x6c71c43d).into(), + }, + PlayerTheme { + cursor: rgba(0x2ba198ff).into(), + selection: rgba(0x2ba1983d).into(), + }, + PlayerTheme { + cursor: rgba(0xdc3330ff).into(), + selection: rgba(0xdc33303d).into(), + }, + PlayerTheme { + cursor: rgba(0xb58902ff).into(), + selection: rgba(0xb589023d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/solarized_light.rs b/crates/theme2/src/themes/solarized_light.rs new file mode 100644 index 0000000000..5c1b732a3c --- /dev/null +++ b/crates/theme2/src/themes/solarized_light.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn solarized_light() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Solarized Light".into(), + is_light: true, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x9faaa8ff).into(), + border_variant: rgba(0x9faaa8ff).into(), + border_focused: rgba(0xbfd3efff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0xcfd0c4ff).into(), + surface: rgba(0xf3eddaff).into(), + background: rgba(0xcfd0c4ff).into(), + filled_element: rgba(0xcfd0c4ff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0xdbe6f6ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0xdbe6f6ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0x002a35ff).into(), + text_muted: rgba(0x34555eff).into(), + text_placeholder: rgba(0xdc3330ff).into(), + text_disabled: rgba(0x6a7f86ff).into(), + text_accent: rgba(0x288bd1ff).into(), + icon_muted: rgba(0x34555eff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("string.escape".into(), rgba(0x30525bff).into()), + ("boolean".into(), rgba(0x849903ff).into()), + ("comment.doc".into(), rgba(0x30525bff).into()), + ("string.special".into(), rgba(0xcb4b17ff).into()), + ("punctuation".into(), rgba(0x04333eff).into()), + ("emphasis".into(), rgba(0x288bd1ff).into()), + ("type".into(), rgba(0x2ba198ff).into()), + ("preproc".into(), rgba(0x002a35ff).into()), + ("emphasis.strong".into(), rgba(0x288bd1ff).into()), + ("constant".into(), rgba(0x849903ff).into()), + ("title".into(), rgba(0x002a35ff).into()), + ("operator".into(), rgba(0xcb4b17ff).into()), + ("punctuation.bracket".into(), rgba(0x04333eff).into()), + ("link_uri".into(), rgba(0x849903ff).into()), + ("label".into(), rgba(0x288bd1ff).into()), + ("enum".into(), rgba(0xcb4b17ff).into()), + ("property".into(), rgba(0x288bd1ff).into()), + ("predictive".into(), rgba(0x679aafff).into()), + ("punctuation.special".into(), rgba(0x04333eff).into()), + ("text.literal".into(), rgba(0xcb4b17ff).into()), + ("string".into(), rgba(0xcb4b17ff).into()), + ("string.regex".into(), rgba(0xcb4b17ff).into()), + ("variable".into(), rgba(0x002a35ff).into()), + ("tag".into(), rgba(0x288bd1ff).into()), + ("string.special.symbol".into(), rgba(0xcb4b17ff).into()), + ("link_text".into(), rgba(0xcb4b17ff).into()), + ("punctuation.list_marker".into(), rgba(0x04333eff).into()), + ("keyword".into(), rgba(0x288bd1ff).into()), + ("constructor".into(), rgba(0x288bd1ff).into()), + ("attribute".into(), rgba(0x288bd1ff).into()), + ("variant".into(), rgba(0x288bd1ff).into()), + ("function".into(), rgba(0xb58903ff).into()), + ("primary".into(), rgba(0x002a35ff).into()), + ("hint".into(), rgba(0x5789a3ff).into()), + ("comment".into(), rgba(0x30525bff).into()), + ("number".into(), rgba(0x849903ff).into()), + ("punctuation.delimiter".into(), rgba(0x04333eff).into()), + ("embedded".into(), rgba(0x002a35ff).into()), + ], + }, + status_bar: rgba(0xcfd0c4ff).into(), + title_bar: rgba(0xcfd0c4ff).into(), + toolbar: rgba(0xfdf6e3ff).into(), + tab_bar: rgba(0xf3eddaff).into(), + editor: rgba(0xfdf6e3ff).into(), + editor_subheader: rgba(0xf3eddaff).into(), + editor_active_line: rgba(0xf3eddaff).into(), + terminal: rgba(0xfdf6e3ff).into(), + image_fallback_background: rgba(0xcfd0c4ff).into(), + git_created: rgba(0x849903ff).into(), + git_modified: rgba(0x288bd1ff).into(), + git_deleted: rgba(0xdc3330ff).into(), + git_conflict: rgba(0xb58903ff).into(), + git_ignored: rgba(0x6a7f86ff).into(), + git_renamed: rgba(0xb58903ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x288bd1ff).into(), + selection: rgba(0x288bd13d).into(), + }, + PlayerTheme { + cursor: rgba(0x849903ff).into(), + selection: rgba(0x8499033d).into(), + }, + PlayerTheme { + cursor: rgba(0xd33781ff).into(), + selection: rgba(0xd337813d).into(), + }, + PlayerTheme { + cursor: rgba(0xcb4b17ff).into(), + selection: rgba(0xcb4b173d).into(), + }, + PlayerTheme { + cursor: rgba(0x6c71c3ff).into(), + selection: rgba(0x6c71c33d).into(), + }, + PlayerTheme { + cursor: rgba(0x2ba198ff).into(), + selection: rgba(0x2ba1983d).into(), + }, + PlayerTheme { + cursor: rgba(0xdc3330ff).into(), + selection: rgba(0xdc33303d).into(), + }, + PlayerTheme { + cursor: rgba(0xb58903ff).into(), + selection: rgba(0xb589033d).into(), + }, + ], + } +} diff --git a/crates/theme2/src/themes/summercamp.rs b/crates/theme2/src/themes/summercamp.rs new file mode 100644 index 0000000000..6eb6bc408a --- /dev/null +++ b/crates/theme2/src/themes/summercamp.rs @@ -0,0 +1,131 @@ + +use gpui2::rgba; + +use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; + +pub fn summercamp() -> Theme { + Theme { + metadata: ThemeMetadata { + name: "Summercamp".into(), + is_light: false, + }, + transparent: rgba(0x00000000).into(), + mac_os_traffic_light_red: rgba(0xec695eff).into(), + mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(), + mac_os_traffic_light_green: rgba(0x61c553ff).into(), + border: rgba(0x302c21ff).into(), + border_variant: rgba(0x302c21ff).into(), + border_focused: rgba(0x193760ff).into(), + border_transparent: rgba(0x00000000).into(), + elevated_surface: rgba(0x2a261cff).into(), + surface: rgba(0x231f16ff).into(), + background: rgba(0x2a261cff).into(), + filled_element: rgba(0x2a261cff).into(), + filled_element_hover: rgba(0xffffff1e).into(), + filled_element_active: rgba(0xffffff28).into(), + filled_element_selected: rgba(0x0e2242ff).into(), + filled_element_disabled: rgba(0x00000000).into(), + ghost_element: rgba(0x00000000).into(), + ghost_element_hover: rgba(0xffffff14).into(), + ghost_element_active: rgba(0xffffff1e).into(), + ghost_element_selected: rgba(0x0e2242ff).into(), + ghost_element_disabled: rgba(0x00000000).into(), + text: rgba(0xf8f5deff).into(), + text_muted: rgba(0x736e55ff).into(), + text_placeholder: rgba(0xe35041ff).into(), + text_disabled: rgba(0x4c4735ff).into(), + text_accent: rgba(0x499befff).into(), + icon_muted: rgba(0x736e55ff).into(), + syntax: SyntaxTheme { + highlights: vec![ + ("predictive".into(), rgba(0x78434aff).into()), + ("title".into(), rgba(0xf8f5deff).into()), + ("primary".into(), rgba(0xf8f5deff).into()), + ("punctuation.special".into(), rgba(0xbfbb9bff).into()), + ("constant".into(), rgba(0x5dea5aff).into()), + ("string.regex".into(), rgba(0xfaa11cff).into()), + ("tag".into(), rgba(0x499befff).into()), + ("preproc".into(), rgba(0xf8f5deff).into()), + ("comment".into(), rgba(0x777159ff).into()), + ("punctuation.bracket".into(), rgba(0xbfbb9bff).into()), + ("constructor".into(), rgba(0x499befff).into()), + ("type".into(), rgba(0x5aeabbff).into()), + ("variable".into(), rgba(0xf8f5deff).into()), + ("operator".into(), rgba(0xfaa11cff).into()), + ("boolean".into(), rgba(0x5dea5aff).into()), + ("attribute".into(), rgba(0x499befff).into()), + ("link_text".into(), rgba(0xfaa11cff).into()), + ("string.escape".into(), rgba(0x777159ff).into()), + ("string.special".into(), rgba(0xfaa11cff).into()), + ("string.special.symbol".into(), rgba(0xfaa11cff).into()), + ("hint".into(), rgba(0x246e61ff).into()), + ("link_uri".into(), rgba(0x5dea5aff).into()), + ("comment.doc".into(), rgba(0x777159ff).into()), + ("emphasis".into(), rgba(0x499befff).into()), + ("punctuation".into(), rgba(0xbfbb9bff).into()), + ("text.literal".into(), rgba(0xfaa11cff).into()), + ("number".into(), rgba(0x5dea5aff).into()), + ("punctuation.delimiter".into(), rgba(0xbfbb9bff).into()), + ("label".into(), rgba(0x499befff).into()), + ("function".into(), rgba(0xf1fe28ff).into()), + ("property".into(), rgba(0x499befff).into()), + ("keyword".into(), rgba(0x499befff).into()), + ("embedded".into(), rgba(0xf8f5deff).into()), + ("string".into(), rgba(0xfaa11cff).into()), + ("punctuation.list_marker".into(), rgba(0xbfbb9bff).into()), + ("enum".into(), rgba(0xfaa11cff).into()), + ("emphasis.strong".into(), rgba(0x499befff).into()), + ("variant".into(), rgba(0x499befff).into()), + ], + }, + status_bar: rgba(0x2a261cff).into(), + title_bar: rgba(0x2a261cff).into(), + toolbar: rgba(0x1b1810ff).into(), + tab_bar: rgba(0x231f16ff).into(), + editor: rgba(0x1b1810ff).into(), + editor_subheader: rgba(0x231f16ff).into(), + editor_active_line: rgba(0x231f16ff).into(), + terminal: rgba(0x1b1810ff).into(), + image_fallback_background: rgba(0x2a261cff).into(), + git_created: rgba(0x5dea5aff).into(), + git_modified: rgba(0x499befff).into(), + git_deleted: rgba(0xe35041ff).into(), + git_conflict: rgba(0xf1fe28ff).into(), + git_ignored: rgba(0x4c4735ff).into(), + git_renamed: rgba(0xf1fe28ff).into(), + players: [ + PlayerTheme { + cursor: rgba(0x499befff).into(), + selection: rgba(0x499bef3d).into(), + }, + PlayerTheme { + cursor: rgba(0x5dea5aff).into(), + selection: rgba(0x5dea5a3d).into(), + }, + PlayerTheme { + cursor: rgba(0xf59be6ff).into(), + selection: rgba(0xf59be63d).into(), + }, + PlayerTheme { + cursor: rgba(0xfaa11cff).into(), + selection: rgba(0xfaa11c3d).into(), + }, + PlayerTheme { + cursor: rgba(0xfe8080ff).into(), + selection: rgba(0xfe80803d).into(), + }, + PlayerTheme { + cursor: rgba(0x5aeabbff).into(), + selection: rgba(0x5aeabb3d).into(), + }, + PlayerTheme { + cursor: rgba(0xe35041ff).into(), + selection: rgba(0xe350413d).into(), + }, + PlayerTheme { + cursor: rgba(0xf1fe28ff).into(), + selection: rgba(0xf1fe283d).into(), + }, + ], + } +} From d3cd5f3ec5d48f74458d98822d4021fe8c093f11 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 11:26:30 -0400 Subject: [PATCH 28/54] Format `theme2` --- crates/theme2/src/themes/andromeda.rs | 1 - crates/theme2/src/themes/atelier_cave_dark.rs | 1 - crates/theme2/src/themes/atelier_cave_light.rs | 1 - crates/theme2/src/themes/atelier_dune_dark.rs | 1 - crates/theme2/src/themes/atelier_dune_light.rs | 1 - crates/theme2/src/themes/atelier_estuary_dark.rs | 1 - crates/theme2/src/themes/atelier_estuary_light.rs | 1 - crates/theme2/src/themes/atelier_forest_dark.rs | 1 - crates/theme2/src/themes/atelier_forest_light.rs | 1 - crates/theme2/src/themes/atelier_heath_dark.rs | 1 - crates/theme2/src/themes/atelier_heath_light.rs | 1 - crates/theme2/src/themes/atelier_lakeside_dark.rs | 1 - crates/theme2/src/themes/atelier_lakeside_light.rs | 1 - crates/theme2/src/themes/atelier_plateau_dark.rs | 1 - crates/theme2/src/themes/atelier_plateau_light.rs | 1 - crates/theme2/src/themes/atelier_savanna_dark.rs | 1 - crates/theme2/src/themes/atelier_savanna_light.rs | 1 - crates/theme2/src/themes/atelier_seaside_dark.rs | 1 - crates/theme2/src/themes/atelier_seaside_light.rs | 1 - crates/theme2/src/themes/atelier_sulphurpool_dark.rs | 1 - crates/theme2/src/themes/atelier_sulphurpool_light.rs | 1 - crates/theme2/src/themes/ayu_dark.rs | 1 - crates/theme2/src/themes/ayu_light.rs | 1 - crates/theme2/src/themes/ayu_mirage.rs | 1 - crates/theme2/src/themes/gruvbox_dark.rs | 1 - crates/theme2/src/themes/gruvbox_dark_hard.rs | 1 - crates/theme2/src/themes/gruvbox_dark_soft.rs | 1 - crates/theme2/src/themes/gruvbox_light.rs | 1 - crates/theme2/src/themes/gruvbox_light_hard.rs | 1 - crates/theme2/src/themes/gruvbox_light_soft.rs | 1 - crates/theme2/src/themes/mod.rs | 1 - crates/theme2/src/themes/one_dark.rs | 1 - crates/theme2/src/themes/one_light.rs | 1 - crates/theme2/src/themes/rose_pine.rs | 1 - crates/theme2/src/themes/rose_pine_dawn.rs | 1 - crates/theme2/src/themes/rose_pine_moon.rs | 1 - crates/theme2/src/themes/sandcastle.rs | 1 - crates/theme2/src/themes/solarized_dark.rs | 1 - crates/theme2/src/themes/solarized_light.rs | 1 - crates/theme2/src/themes/summercamp.rs | 1 - 40 files changed, 40 deletions(-) diff --git a/crates/theme2/src/themes/andromeda.rs b/crates/theme2/src/themes/andromeda.rs index b5cabfedfa..6afd7edd4d 100644 --- a/crates/theme2/src/themes/andromeda.rs +++ b/crates/theme2/src/themes/andromeda.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_cave_dark.rs b/crates/theme2/src/themes/atelier_cave_dark.rs index e3926e6d36..c5190f4e98 100644 --- a/crates/theme2/src/themes/atelier_cave_dark.rs +++ b/crates/theme2/src/themes/atelier_cave_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_cave_light.rs b/crates/theme2/src/themes/atelier_cave_light.rs index e21dd12c4a..ae2e912f14 100644 --- a/crates/theme2/src/themes/atelier_cave_light.rs +++ b/crates/theme2/src/themes/atelier_cave_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_dune_dark.rs b/crates/theme2/src/themes/atelier_dune_dark.rs index 313b4df261..03d0c5eea0 100644 --- a/crates/theme2/src/themes/atelier_dune_dark.rs +++ b/crates/theme2/src/themes/atelier_dune_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_dune_light.rs b/crates/theme2/src/themes/atelier_dune_light.rs index c7dfd884cf..1d0f944916 100644 --- a/crates/theme2/src/themes/atelier_dune_light.rs +++ b/crates/theme2/src/themes/atelier_dune_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_estuary_dark.rs b/crates/theme2/src/themes/atelier_estuary_dark.rs index 9dfa5d37a9..ad5c9fbc1e 100644 --- a/crates/theme2/src/themes/atelier_estuary_dark.rs +++ b/crates/theme2/src/themes/atelier_estuary_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_estuary_light.rs b/crates/theme2/src/themes/atelier_estuary_light.rs index 9e79b6bce0..91eaa88fab 100644 --- a/crates/theme2/src/themes/atelier_estuary_light.rs +++ b/crates/theme2/src/themes/atelier_estuary_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_forest_dark.rs b/crates/theme2/src/themes/atelier_forest_dark.rs index 2e8cef56de..83228e671f 100644 --- a/crates/theme2/src/themes/atelier_forest_dark.rs +++ b/crates/theme2/src/themes/atelier_forest_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_forest_light.rs b/crates/theme2/src/themes/atelier_forest_light.rs index 94d525835d..882d5c2fcb 100644 --- a/crates/theme2/src/themes/atelier_forest_light.rs +++ b/crates/theme2/src/themes/atelier_forest_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_heath_dark.rs b/crates/theme2/src/themes/atelier_heath_dark.rs index c7e9590689..354c98069f 100644 --- a/crates/theme2/src/themes/atelier_heath_dark.rs +++ b/crates/theme2/src/themes/atelier_heath_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_heath_light.rs b/crates/theme2/src/themes/atelier_heath_light.rs index 540f84febf..f1a9e4d8c6 100644 --- a/crates/theme2/src/themes/atelier_heath_light.rs +++ b/crates/theme2/src/themes/atelier_heath_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_lakeside_dark.rs b/crates/theme2/src/themes/atelier_lakeside_dark.rs index 015a9d0751..61b78864b7 100644 --- a/crates/theme2/src/themes/atelier_lakeside_dark.rs +++ b/crates/theme2/src/themes/atelier_lakeside_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_lakeside_light.rs b/crates/theme2/src/themes/atelier_lakeside_light.rs index 85d5e3d782..64fb70dadb 100644 --- a/crates/theme2/src/themes/atelier_lakeside_light.rs +++ b/crates/theme2/src/themes/atelier_lakeside_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_plateau_dark.rs b/crates/theme2/src/themes/atelier_plateau_dark.rs index 231572ad65..0ba5a1659d 100644 --- a/crates/theme2/src/themes/atelier_plateau_dark.rs +++ b/crates/theme2/src/themes/atelier_plateau_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_plateau_light.rs b/crates/theme2/src/themes/atelier_plateau_light.rs index efca15d7d6..68f100dd85 100644 --- a/crates/theme2/src/themes/atelier_plateau_light.rs +++ b/crates/theme2/src/themes/atelier_plateau_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_savanna_dark.rs b/crates/theme2/src/themes/atelier_savanna_dark.rs index 7314824f18..d4040db958 100644 --- a/crates/theme2/src/themes/atelier_savanna_dark.rs +++ b/crates/theme2/src/themes/atelier_savanna_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_savanna_light.rs b/crates/theme2/src/themes/atelier_savanna_light.rs index 32df2e1691..08722cd91c 100644 --- a/crates/theme2/src/themes/atelier_savanna_light.rs +++ b/crates/theme2/src/themes/atelier_savanna_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_seaside_dark.rs b/crates/theme2/src/themes/atelier_seaside_dark.rs index 6597e31de6..475115e0d1 100644 --- a/crates/theme2/src/themes/atelier_seaside_dark.rs +++ b/crates/theme2/src/themes/atelier_seaside_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_seaside_light.rs b/crates/theme2/src/themes/atelier_seaside_light.rs index a515bb4a71..557134b540 100644 --- a/crates/theme2/src/themes/atelier_seaside_light.rs +++ b/crates/theme2/src/themes/atelier_seaside_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_sulphurpool_dark.rs b/crates/theme2/src/themes/atelier_sulphurpool_dark.rs index 0c01560f22..8be8451740 100644 --- a/crates/theme2/src/themes/atelier_sulphurpool_dark.rs +++ b/crates/theme2/src/themes/atelier_sulphurpool_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/atelier_sulphurpool_light.rs b/crates/theme2/src/themes/atelier_sulphurpool_light.rs index 16b491b6ba..dba723331a 100644 --- a/crates/theme2/src/themes/atelier_sulphurpool_light.rs +++ b/crates/theme2/src/themes/atelier_sulphurpool_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/ayu_dark.rs b/crates/theme2/src/themes/ayu_dark.rs index 88f3f93576..35d3a43154 100644 --- a/crates/theme2/src/themes/ayu_dark.rs +++ b/crates/theme2/src/themes/ayu_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/ayu_light.rs b/crates/theme2/src/themes/ayu_light.rs index 761eece82a..887282e564 100644 --- a/crates/theme2/src/themes/ayu_light.rs +++ b/crates/theme2/src/themes/ayu_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/ayu_mirage.rs b/crates/theme2/src/themes/ayu_mirage.rs index cd74529713..2974881a18 100644 --- a/crates/theme2/src/themes/ayu_mirage.rs +++ b/crates/theme2/src/themes/ayu_mirage.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/gruvbox_dark.rs b/crates/theme2/src/themes/gruvbox_dark.rs index 1f32e820c9..6e982808cf 100644 --- a/crates/theme2/src/themes/gruvbox_dark.rs +++ b/crates/theme2/src/themes/gruvbox_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/gruvbox_dark_hard.rs b/crates/theme2/src/themes/gruvbox_dark_hard.rs index cf7875b33e..159ab28325 100644 --- a/crates/theme2/src/themes/gruvbox_dark_hard.rs +++ b/crates/theme2/src/themes/gruvbox_dark_hard.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/gruvbox_dark_soft.rs b/crates/theme2/src/themes/gruvbox_dark_soft.rs index f0e1c44e30..6a6423389e 100644 --- a/crates/theme2/src/themes/gruvbox_dark_soft.rs +++ b/crates/theme2/src/themes/gruvbox_dark_soft.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/gruvbox_light.rs b/crates/theme2/src/themes/gruvbox_light.rs index 76e35bd0b6..7582f8bd8a 100644 --- a/crates/theme2/src/themes/gruvbox_light.rs +++ b/crates/theme2/src/themes/gruvbox_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/gruvbox_light_hard.rs b/crates/theme2/src/themes/gruvbox_light_hard.rs index 8438e0f893..e5e3fe54cf 100644 --- a/crates/theme2/src/themes/gruvbox_light_hard.rs +++ b/crates/theme2/src/themes/gruvbox_light_hard.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/gruvbox_light_soft.rs b/crates/theme2/src/themes/gruvbox_light_soft.rs index d420b580f8..15574e2960 100644 --- a/crates/theme2/src/themes/gruvbox_light_soft.rs +++ b/crates/theme2/src/themes/gruvbox_light_soft.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/mod.rs b/crates/theme2/src/themes/mod.rs index 018ec7c939..17cd5ac6e0 100644 --- a/crates/theme2/src/themes/mod.rs +++ b/crates/theme2/src/themes/mod.rs @@ -1,4 +1,3 @@ - mod andromeda; mod atelier_cave_dark; mod atelier_cave_light; diff --git a/crates/theme2/src/themes/one_dark.rs b/crates/theme2/src/themes/one_dark.rs index e81082a2d8..c7408d1820 100644 --- a/crates/theme2/src/themes/one_dark.rs +++ b/crates/theme2/src/themes/one_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/one_light.rs b/crates/theme2/src/themes/one_light.rs index 05528d6a55..ee802d57d3 100644 --- a/crates/theme2/src/themes/one_light.rs +++ b/crates/theme2/src/themes/one_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/rose_pine.rs b/crates/theme2/src/themes/rose_pine.rs index cbe88144ed..f3bd454cdc 100644 --- a/crates/theme2/src/themes/rose_pine.rs +++ b/crates/theme2/src/themes/rose_pine.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/rose_pine_dawn.rs b/crates/theme2/src/themes/rose_pine_dawn.rs index 66dde6730f..ba64bf9d99 100644 --- a/crates/theme2/src/themes/rose_pine_dawn.rs +++ b/crates/theme2/src/themes/rose_pine_dawn.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/rose_pine_moon.rs b/crates/theme2/src/themes/rose_pine_moon.rs index ce96003705..167b78afb5 100644 --- a/crates/theme2/src/themes/rose_pine_moon.rs +++ b/crates/theme2/src/themes/rose_pine_moon.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/sandcastle.rs b/crates/theme2/src/themes/sandcastle.rs index 2004033239..7fa0a27fb3 100644 --- a/crates/theme2/src/themes/sandcastle.rs +++ b/crates/theme2/src/themes/sandcastle.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/solarized_dark.rs b/crates/theme2/src/themes/solarized_dark.rs index 1c58ef6008..2e381a6e95 100644 --- a/crates/theme2/src/themes/solarized_dark.rs +++ b/crates/theme2/src/themes/solarized_dark.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/solarized_light.rs b/crates/theme2/src/themes/solarized_light.rs index 5c1b732a3c..a959a0a9d1 100644 --- a/crates/theme2/src/themes/solarized_light.rs +++ b/crates/theme2/src/themes/solarized_light.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; diff --git a/crates/theme2/src/themes/summercamp.rs b/crates/theme2/src/themes/summercamp.rs index 6eb6bc408a..c1e66aedd1 100644 --- a/crates/theme2/src/themes/summercamp.rs +++ b/crates/theme2/src/themes/summercamp.rs @@ -1,4 +1,3 @@ - use gpui2::rgba; use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; From b31a004def79e4472a65f8599b109e8842bb5236 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 12:56:23 -0400 Subject: [PATCH 29/54] Add `menu2` crate --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + crates/menu2/Cargo.toml | 12 ++++++++++++ crates/menu2/src/menu2.rs | 25 +++++++++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 crates/menu2/Cargo.toml create mode 100644 crates/menu2/src/menu2.rs diff --git a/Cargo.lock b/Cargo.lock index dc5684e115..0caaaeceef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4795,6 +4795,13 @@ dependencies = [ "gpui", ] +[[package]] +name = "menu2" +version = "0.1.0" +dependencies = [ + "gpui2", +] + [[package]] name = "metal" version = "0.21.0" diff --git a/Cargo.toml b/Cargo.toml index 2627afb4ba..ac490ce935 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ members = [ "crates/lsp2", "crates/media", "crates/menu", + "crates/menu2", "crates/multi_buffer", "crates/node_runtime", "crates/notifications", diff --git a/crates/menu2/Cargo.toml b/crates/menu2/Cargo.toml new file mode 100644 index 0000000000..c366de6866 --- /dev/null +++ b/crates/menu2/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "menu2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/menu2.rs" +doctest = false + +[dependencies] +gpui2 = { path = "../gpui2" } diff --git a/crates/menu2/src/menu2.rs b/crates/menu2/src/menu2.rs new file mode 100644 index 0000000000..decd4aca22 --- /dev/null +++ b/crates/menu2/src/menu2.rs @@ -0,0 +1,25 @@ +// todo!(use actions! macro) + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Cancel; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Confirm; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct SecondaryConfirm; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct SelectPrev; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct SelectNext; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct SelectFirst; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct SelectLast; + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ShowContextMenu; From dc8a8538421102c1f41cbec1f79f5130ecb7ed35 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:27:05 +0100 Subject: [PATCH 30/54] lsp/next-ls: Fix wrong nls binary being fetched. (#3181) CPU types had to be swapped around. Fixed zed-industries/community#2185 Release Notes: - Fixed Elixir next-ls LSP installation failing due to fetching a binary for the wrong architecture (zed-industries/community#2185). --- crates/zed/src/languages/elixir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index 5c0ff273ae..df438d89ee 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -321,8 +321,8 @@ impl LspAdapter for NextLspAdapter { latest_github_release("elixir-tools/next-ls", false, delegate.http_client()).await?; let version = release.name.clone(); let platform = match consts::ARCH { - "x86_64" => "darwin_arm64", - "aarch64" => "darwin_amd64", + "x86_64" => "darwin_amd64", + "aarch64" => "darwin_arm64", other => bail!("Running on unsupported platform: {other}"), }; let asset_name = format!("next_ls_{}", platform); From e63a611c81815fb991c9001ce380d349673becf0 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:30:27 +0100 Subject: [PATCH 31/54] lsp/next-ls: Fix wrong nls binary being fetched. CPU types had to be swapped around. Fixed zed-industries/community#2185 --- crates/zed/src/languages/elixir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index 5c0ff273ae..df438d89ee 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -321,8 +321,8 @@ impl LspAdapter for NextLspAdapter { latest_github_release("elixir-tools/next-ls", false, delegate.http_client()).await?; let version = release.name.clone(); let platform = match consts::ARCH { - "x86_64" => "darwin_arm64", - "aarch64" => "darwin_amd64", + "x86_64" => "darwin_amd64", + "aarch64" => "darwin_arm64", other => bail!("Running on unsupported platform: {other}"), }; let asset_name = format!("next_ls_{}", platform); From 7b4e699d0ee4aa4215c134f6b7db332cc890bd91 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 14:28:25 -0400 Subject: [PATCH 32/54] Remove `themed` wrapper --- crates/storybook2/src/storybook2.rs | 21 +++---- crates/ui2/src/prelude.rs | 2 +- crates/ui2/src/theme.rs | 89 +---------------------------- 3 files changed, 11 insertions(+), 101 deletions(-) diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index a06a1392b2..493997ccfe 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -18,7 +18,7 @@ use settings2::{default_settings, Settings, SettingsStore}; use simplelog::SimpleLogger; use story_selector::ComponentStory; use theme2::{ThemeRegistry, ThemeSettings}; -use ui::{prelude::*, themed}; +use ui::prelude::*; use crate::assets::Assets; use crate::story_selector::StorySelector; @@ -86,7 +86,7 @@ fn main() { }, move |cx| { cx.build_view( - |cx| StoryWrapper::new(selector.story(cx), theme), + |cx| StoryWrapper::new(selector.story(cx)), StoryWrapper::render, ) }, @@ -99,22 +99,19 @@ fn main() { #[derive(Clone)] pub struct StoryWrapper { story: AnyView, - theme: Theme, } impl StoryWrapper { - pub(crate) fn new(story: AnyView, theme: Theme) -> Self { - Self { story, theme } + pub(crate) fn new(story: AnyView) -> Self { + Self { story } } fn render(&mut self, cx: &mut ViewContext) -> impl Component { - themed(self.theme.clone(), cx, |cx| { - div() - .flex() - .flex_col() - .size_full() - .child(self.story.clone()) - }) + div() + .flex() + .flex_col() + .size_full() + .child(self.story.clone()) } } diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index b8143b6e50..95cd6ffa6d 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -5,7 +5,7 @@ pub use gpui2::{ pub use crate::elevation::*; use crate::settings::user_settings; -pub use crate::{old_theme, theme, ButtonVariant, Theme}; +pub use crate::{old_theme, theme, ButtonVariant}; use gpui2::{rems, Hsla, Rems}; use strum::EnumIter; diff --git a/crates/ui2/src/theme.rs b/crates/ui2/src/theme.rs index cc46ddcb17..f160a24da3 100644 --- a/crates/ui2/src/theme.rs +++ b/crates/ui2/src/theme.rs @@ -1,7 +1,4 @@ -use gpui2::{ - AnyElement, AppContext, Bounds, Component, Element, Hsla, LayoutId, Pixels, Result, - ViewContext, WindowContext, -}; +use gpui2::{AppContext, Hsla, Result, WindowContext}; use serde::{de::Visitor, Deserialize, Deserializer}; use std::collections::HashMap; use std::fmt; @@ -132,90 +129,6 @@ where deserializer.deserialize_map(SyntaxVisitor) } -pub fn themed(theme: Theme, cx: &mut ViewContext, build_child: F) -> Themed -where - V: 'static, - E: Element, - F: FnOnce(&mut ViewContext) -> E, -{ - cx.default_global::().0.push(theme.clone()); - let child = build_child(cx); - cx.default_global::().0.pop(); - Themed { theme, child } -} - -pub struct Themed { - pub(crate) theme: Theme, - pub(crate) child: E, -} - -impl Component for Themed -where - V: 'static, - E: 'static + Element + Send, - E::ElementState: Send, -{ - fn render(self) -> AnyElement { - AnyElement::new(self) - } -} - -#[derive(Default)] -struct ThemeStack(Vec); - -impl + Send> Element for Themed -where - V: 'static, - E::ElementState: Send, -{ - type ElementState = E::ElementState; - - fn id(&self) -> Option { - None - } - - fn initialize( - &mut self, - view_state: &mut V, - element_state: Option, - cx: &mut ViewContext, - ) -> Self::ElementState { - cx.default_global::().0.push(self.theme.clone()); - let element_state = self.child.initialize(view_state, element_state, cx); - cx.default_global::().0.pop(); - element_state - } - - fn layout( - &mut self, - view_state: &mut V, - element_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) -> LayoutId - where - Self: Sized, - { - cx.default_global::().0.push(self.theme.clone()); - let layout_id = self.child.layout(view_state, element_state, cx); - cx.default_global::().0.pop(); - layout_id - } - - fn paint( - &mut self, - bounds: Bounds, - view_state: &mut V, - frame_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) where - Self: Sized, - { - cx.default_global::().0.push(self.theme.clone()); - self.child.paint(bounds, view_state, frame_state, cx); - cx.default_global::().0.pop(); - } -} - pub fn old_theme(cx: &WindowContext) -> Arc { Arc::new(cx.global::().clone()) } From 14d24a9ac6b67c82359921aae5436bea5e38584f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 14:36:49 -0400 Subject: [PATCH 33/54] Remove references to `old_theme` --- crates/ui2/src/components/workspace.rs | 11 +++++------ crates/ui2/src/elements/icon.rs | 21 ++++++++++----------- crates/ui2/src/elements/label.rs | 14 +++++--------- crates/ui2/src/prelude.rs | 2 +- crates/ui2/src/theme.rs | 6 +----- 5 files changed, 22 insertions(+), 32 deletions(-) diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index fbb2c64668..af2485a7a9 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -3,8 +3,7 @@ use std::sync::Arc; use chrono::DateTime; use gpui2::{px, relative, rems, AppContext, Context, Size, View}; -use crate::{ - old_theme, static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage, +use crate::{static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, CollabPanel, EditorPane, FakeSettings, Label, LanguageSelector, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, ProjectPanel, SettingValue, SplitDirection, StatusBar, Terminal, TitleBar, Toast, ToastOrigin, @@ -179,7 +178,7 @@ impl Workspace { } pub fn render(&mut self, cx: &mut ViewContext) -> impl Component { - let theme = old_theme(cx).clone(); + let theme = theme(cx); // HACK: This should happen inside of `debug_toggle_user_settings`, but // we don't have `cx.global::()` in event handlers at the moment. @@ -216,8 +215,8 @@ impl Workspace { .gap_0() .justify_start() .items_start() - .text_color(theme.lowest.base.default.foreground) - .bg(theme.lowest.base.default.background) + .text_color(theme.text) + .bg(theme.background) .child(self.title_bar.clone()) .child( div() @@ -228,7 +227,7 @@ impl Workspace { .overflow_hidden() .border_t() .border_b() - .border_color(theme.lowest.base.default.border) + .border_color(theme.border) .children( Some( Panel::new("project-panel-outer", cx) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index b47e4c6608..ef36442296 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -2,7 +2,6 @@ use gpui2::{svg, Hsla}; use strum::EnumIter; use crate::prelude::*; -use crate::theme::old_theme; #[derive(Default, PartialEq, Copy, Clone)] pub enum IconSize { @@ -27,17 +26,17 @@ pub enum IconColor { impl IconColor { pub fn color(self, cx: &WindowContext) -> Hsla { - let theme = old_theme(cx); + let theme = theme(cx); match self { - IconColor::Default => theme.lowest.base.default.foreground, - IconColor::Muted => theme.lowest.variant.default.foreground, - IconColor::Disabled => theme.lowest.base.disabled.foreground, - IconColor::Placeholder => theme.lowest.base.disabled.foreground, - IconColor::Accent => theme.lowest.accent.default.foreground, - IconColor::Error => theme.lowest.negative.default.foreground, - IconColor::Warning => theme.lowest.warning.default.foreground, - IconColor::Success => theme.lowest.positive.default.foreground, - IconColor::Info => theme.lowest.accent.default.foreground, + IconColor::Default => gpui2::red(), + IconColor::Muted => gpui2::red(), + IconColor::Disabled => gpui2::red(), + IconColor::Placeholder => gpui2::red(), + IconColor::Accent => gpui2::red(), + IconColor::Error => gpui2::red(), + IconColor::Warning => gpui2::red(), + IconColor::Success => gpui2::red(), + IconColor::Info => gpui2::red() } } } diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index f4014d5922..6c97819eeb 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -2,8 +2,6 @@ use gpui2::{relative, Hsla, WindowContext}; use smallvec::SmallVec; use crate::prelude::*; -use crate::theme::old_theme; - #[derive(Default, PartialEq, Copy, Clone)] pub enum LabelColor { #[default] @@ -21,19 +19,17 @@ pub enum LabelColor { impl LabelColor { pub fn hsla(&self, cx: &WindowContext) -> Hsla { let theme = theme(cx); - // TODO: Remove - let old_theme = old_theme(cx); match self { Self::Default => theme.text, Self::Muted => theme.text_muted, - Self::Created => old_theme.middle.positive.default.foreground, - Self::Modified => old_theme.middle.warning.default.foreground, - Self::Deleted => old_theme.middle.negative.default.foreground, + Self::Created => gpui2::red(), + Self::Modified => gpui2::red(), + Self::Deleted => gpui2::red(), Self::Disabled => theme.text_disabled, - Self::Hidden => old_theme.middle.variant.default.foreground, + Self::Hidden => gpui2::red(), Self::Placeholder => theme.text_placeholder, - Self::Accent => old_theme.middle.accent.default.foreground, + Self::Accent => gpui2::red(), } } } diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 95cd6ffa6d..5d701fc5d7 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -5,7 +5,7 @@ pub use gpui2::{ pub use crate::elevation::*; use crate::settings::user_settings; -pub use crate::{old_theme, theme, ButtonVariant}; +pub use crate::{theme, ButtonVariant}; use gpui2::{rems, Hsla, Rems}; use strum::EnumIter; diff --git a/crates/ui2/src/theme.rs b/crates/ui2/src/theme.rs index f160a24da3..98c93f9bdd 100644 --- a/crates/ui2/src/theme.rs +++ b/crates/ui2/src/theme.rs @@ -1,4 +1,4 @@ -use gpui2::{AppContext, Hsla, Result, WindowContext}; +use gpui2::{AppContext, Hsla, Result}; use serde::{de::Visitor, Deserialize, Deserializer}; use std::collections::HashMap; use std::fmt; @@ -129,10 +129,6 @@ where deserializer.deserialize_map(SyntaxVisitor) } -pub fn old_theme(cx: &WindowContext) -> Arc { - Arc::new(cx.global::().clone()) -} - pub fn theme(cx: &AppContext) -> Arc { theme2::active_theme(cx).clone() } From 04ab68502b950e7d23c0522347b586ebd3670a4f Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 14:40:31 -0400 Subject: [PATCH 34/54] port ai crate to ai2, with all tests passing --- Cargo.lock | 28 ++ crates/Cargo.toml | 38 ++ crates/ai2/Cargo.toml | 38 ++ crates/ai2/src/ai2.rs | 8 + crates/ai2/src/auth.rs | 17 + crates/ai2/src/completion.rs | 23 ++ crates/ai2/src/embedding.rs | 123 +++++++ crates/ai2/src/models.rs | 16 + crates/ai2/src/prompts/base.rs | 330 ++++++++++++++++++ crates/ai2/src/prompts/file_context.rs | 164 +++++++++ crates/ai2/src/prompts/generate.rs | 99 ++++++ crates/ai2/src/prompts/mod.rs | 5 + crates/ai2/src/prompts/preamble.rs | 52 +++ crates/ai2/src/prompts/repository_context.rs | 98 ++++++ crates/ai2/src/providers/mod.rs | 1 + .../ai2/src/providers/open_ai/completion.rs | 306 ++++++++++++++++ crates/ai2/src/providers/open_ai/embedding.rs | 313 +++++++++++++++++ crates/ai2/src/providers/open_ai/mod.rs | 9 + crates/ai2/src/providers/open_ai/model.rs | 57 +++ crates/ai2/src/providers/open_ai/new.rs | 11 + crates/ai2/src/test.rs | 193 ++++++++++ crates/zed2/Cargo.toml | 1 + 22 files changed, 1930 insertions(+) create mode 100644 crates/Cargo.toml create mode 100644 crates/ai2/Cargo.toml create mode 100644 crates/ai2/src/ai2.rs create mode 100644 crates/ai2/src/auth.rs create mode 100644 crates/ai2/src/completion.rs create mode 100644 crates/ai2/src/embedding.rs create mode 100644 crates/ai2/src/models.rs create mode 100644 crates/ai2/src/prompts/base.rs create mode 100644 crates/ai2/src/prompts/file_context.rs create mode 100644 crates/ai2/src/prompts/generate.rs create mode 100644 crates/ai2/src/prompts/mod.rs create mode 100644 crates/ai2/src/prompts/preamble.rs create mode 100644 crates/ai2/src/prompts/repository_context.rs create mode 100644 crates/ai2/src/providers/mod.rs create mode 100644 crates/ai2/src/providers/open_ai/completion.rs create mode 100644 crates/ai2/src/providers/open_ai/embedding.rs create mode 100644 crates/ai2/src/providers/open_ai/mod.rs create mode 100644 crates/ai2/src/providers/open_ai/model.rs create mode 100644 crates/ai2/src/providers/open_ai/new.rs create mode 100644 crates/ai2/src/test.rs diff --git a/Cargo.lock b/Cargo.lock index 0caaaeceef..a5d187d08e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,33 @@ dependencies = [ "util", ] +[[package]] +name = "ai2" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "futures 0.3.28", + "gpui2", + "isahc", + "language2", + "lazy_static", + "log", + "matrixmultiply", + "ordered-float 2.10.0", + "parking_lot 0.11.2", + "parse_duration", + "postage", + "rand 0.8.5", + "regex", + "rusqlite", + "serde", + "serde_json", + "tiktoken-rs", + "util", +] + [[package]] name = "alacritty_config" version = "0.1.2-dev" @@ -10903,6 +10930,7 @@ dependencies = [ name = "zed2" version = "0.109.0" dependencies = [ + "ai2", "anyhow", "async-compression", "async-recursion 0.3.2", diff --git a/crates/Cargo.toml b/crates/Cargo.toml new file mode 100644 index 0000000000..fb49a4b515 --- /dev/null +++ b/crates/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "ai" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/ai.rs" +doctest = false + +[features] +test-support = [] + +[dependencies] +gpui = { path = "../gpui" } +util = { path = "../util" } +language = { path = "../language" } +async-trait.workspace = true +anyhow.workspace = true +futures.workspace = true +lazy_static.workspace = true +ordered-float.workspace = true +parking_lot.workspace = true +isahc.workspace = true +regex.workspace = true +serde.workspace = true +serde_json.workspace = true +postage.workspace = true +rand.workspace = true +log.workspace = true +parse_duration = "2.1.1" +tiktoken-rs = "0.5.0" +matrixmultiply = "0.3.7" +rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] } +bincode = "1.3.3" + +[dev-dependencies] +gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/ai2/Cargo.toml b/crates/ai2/Cargo.toml new file mode 100644 index 0000000000..4f06840e8e --- /dev/null +++ b/crates/ai2/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "ai2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/ai2.rs" +doctest = false + +[features] +test-support = [] + +[dependencies] +gpui2 = { path = "../gpui2" } +util = { path = "../util" } +language2 = { path = "../language2" } +async-trait.workspace = true +anyhow.workspace = true +futures.workspace = true +lazy_static.workspace = true +ordered-float.workspace = true +parking_lot.workspace = true +isahc.workspace = true +regex.workspace = true +serde.workspace = true +serde_json.workspace = true +postage.workspace = true +rand.workspace = true +log.workspace = true +parse_duration = "2.1.1" +tiktoken-rs = "0.5.0" +matrixmultiply = "0.3.7" +rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] } +bincode = "1.3.3" + +[dev-dependencies] +gpui2 = { path = "../gpui2", features = ["test-support"] } diff --git a/crates/ai2/src/ai2.rs b/crates/ai2/src/ai2.rs new file mode 100644 index 0000000000..dda22d2a1d --- /dev/null +++ b/crates/ai2/src/ai2.rs @@ -0,0 +1,8 @@ +pub mod auth; +pub mod completion; +pub mod embedding; +pub mod models; +pub mod prompts; +pub mod providers; +#[cfg(any(test, feature = "test-support"))] +pub mod test; diff --git a/crates/ai2/src/auth.rs b/crates/ai2/src/auth.rs new file mode 100644 index 0000000000..e4670bb449 --- /dev/null +++ b/crates/ai2/src/auth.rs @@ -0,0 +1,17 @@ +use async_trait::async_trait; +use gpui2::AppContext; + +#[derive(Clone, Debug)] +pub enum ProviderCredential { + Credentials { api_key: String }, + NoCredentials, + NotNeeded, +} + +#[async_trait] +pub trait CredentialProvider: Send + Sync { + fn has_credentials(&self) -> bool; + async fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential; + async fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential); + async fn delete_credentials(&self, cx: &mut AppContext); +} diff --git a/crates/ai2/src/completion.rs b/crates/ai2/src/completion.rs new file mode 100644 index 0000000000..30a60fcf1d --- /dev/null +++ b/crates/ai2/src/completion.rs @@ -0,0 +1,23 @@ +use anyhow::Result; +use futures::{future::BoxFuture, stream::BoxStream}; + +use crate::{auth::CredentialProvider, models::LanguageModel}; + +pub trait CompletionRequest: Send + Sync { + fn data(&self) -> serde_json::Result; +} + +pub trait CompletionProvider: CredentialProvider { + fn base_model(&self) -> Box; + fn complete( + &self, + prompt: Box, + ) -> BoxFuture<'static, Result>>>; + fn box_clone(&self) -> Box; +} + +impl Clone for Box { + fn clone(&self) -> Box { + self.box_clone() + } +} diff --git a/crates/ai2/src/embedding.rs b/crates/ai2/src/embedding.rs new file mode 100644 index 0000000000..7ea4786178 --- /dev/null +++ b/crates/ai2/src/embedding.rs @@ -0,0 +1,123 @@ +use std::time::Instant; + +use anyhow::Result; +use async_trait::async_trait; +use ordered_float::OrderedFloat; +use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef}; +use rusqlite::ToSql; + +use crate::auth::CredentialProvider; +use crate::models::LanguageModel; + +#[derive(Debug, PartialEq, Clone)] +pub struct Embedding(pub Vec); + +// This is needed for semantic index functionality +// Unfortunately it has to live wherever the "Embedding" struct is created. +// Keeping this in here though, introduces a 'rusqlite' dependency into AI +// which is less than ideal +impl FromSql for Embedding { + fn column_result(value: ValueRef) -> FromSqlResult { + let bytes = value.as_blob()?; + let embedding: Result, Box> = bincode::deserialize(bytes); + if embedding.is_err() { + return Err(rusqlite::types::FromSqlError::Other(embedding.unwrap_err())); + } + Ok(Embedding(embedding.unwrap())) + } +} + +impl ToSql for Embedding { + fn to_sql(&self) -> rusqlite::Result { + let bytes = bincode::serialize(&self.0) + .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?; + Ok(ToSqlOutput::Owned(rusqlite::types::Value::Blob(bytes))) + } +} +impl From> for Embedding { + fn from(value: Vec) -> Self { + Embedding(value) + } +} + +impl Embedding { + pub fn similarity(&self, other: &Self) -> OrderedFloat { + let len = self.0.len(); + assert_eq!(len, other.0.len()); + + let mut result = 0.0; + unsafe { + matrixmultiply::sgemm( + 1, + len, + 1, + 1.0, + self.0.as_ptr(), + len as isize, + 1, + other.0.as_ptr(), + 1, + len as isize, + 0.0, + &mut result as *mut f32, + 1, + 1, + ); + } + OrderedFloat(result) + } +} + +#[async_trait] +pub trait EmbeddingProvider: CredentialProvider { + fn base_model(&self) -> Box; + async fn embed_batch(&self, spans: Vec) -> Result>; + fn max_tokens_per_batch(&self) -> usize; + fn rate_limit_expiration(&self) -> Option; +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::prelude::*; + + #[gpui2::test] + fn test_similarity(mut rng: StdRng) { + assert_eq!( + Embedding::from(vec![1., 0., 0., 0., 0.]) + .similarity(&Embedding::from(vec![0., 1., 0., 0., 0.])), + 0. + ); + assert_eq!( + Embedding::from(vec![2., 0., 0., 0., 0.]) + .similarity(&Embedding::from(vec![3., 1., 0., 0., 0.])), + 6. + ); + + for _ in 0..100 { + let size = 1536; + let mut a = vec![0.; size]; + let mut b = vec![0.; size]; + for (a, b) in a.iter_mut().zip(b.iter_mut()) { + *a = rng.gen(); + *b = rng.gen(); + } + let a = Embedding::from(a); + let b = Embedding::from(b); + + assert_eq!( + round_to_decimals(a.similarity(&b), 1), + round_to_decimals(reference_dot(&a.0, &b.0), 1) + ); + } + + fn round_to_decimals(n: OrderedFloat, decimal_places: i32) -> f32 { + let factor = (10.0 as f32).powi(decimal_places); + (n * factor).round() / factor + } + + fn reference_dot(a: &[f32], b: &[f32]) -> OrderedFloat { + OrderedFloat(a.iter().zip(b.iter()).map(|(a, b)| a * b).sum()) + } + } +} diff --git a/crates/ai2/src/models.rs b/crates/ai2/src/models.rs new file mode 100644 index 0000000000..1db3d58c6f --- /dev/null +++ b/crates/ai2/src/models.rs @@ -0,0 +1,16 @@ +pub enum TruncationDirection { + Start, + End, +} + +pub trait LanguageModel { + fn name(&self) -> String; + fn count_tokens(&self, content: &str) -> anyhow::Result; + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result; + fn capacity(&self) -> anyhow::Result; +} diff --git a/crates/ai2/src/prompts/base.rs b/crates/ai2/src/prompts/base.rs new file mode 100644 index 0000000000..29091d0f5b --- /dev/null +++ b/crates/ai2/src/prompts/base.rs @@ -0,0 +1,330 @@ +use std::cmp::Reverse; +use std::ops::Range; +use std::sync::Arc; + +use language2::BufferSnapshot; +use util::ResultExt; + +use crate::models::LanguageModel; +use crate::prompts::repository_context::PromptCodeSnippet; + +pub(crate) enum PromptFileType { + Text, + Code, +} + +// TODO: Set this up to manage for defaults well +pub struct PromptArguments { + pub model: Arc, + pub user_prompt: Option, + pub language_name: Option, + pub project_name: Option, + pub snippets: Vec, + pub reserved_tokens: usize, + pub buffer: Option, + pub selected_range: Option>, +} + +impl PromptArguments { + pub(crate) fn get_file_type(&self) -> PromptFileType { + if self + .language_name + .as_ref() + .and_then(|name| Some(!["Markdown", "Plain Text"].contains(&name.as_str()))) + .unwrap_or(true) + { + PromptFileType::Code + } else { + PromptFileType::Text + } + } +} + +pub trait PromptTemplate { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)>; +} + +#[repr(i8)] +#[derive(PartialEq, Eq, Ord)] +pub enum PromptPriority { + Mandatory, // Ignores truncation + Ordered { order: usize }, // Truncates based on priority +} + +impl PartialOrd for PromptPriority { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::Mandatory, Self::Mandatory) => Some(std::cmp::Ordering::Equal), + (Self::Mandatory, Self::Ordered { .. }) => Some(std::cmp::Ordering::Greater), + (Self::Ordered { .. }, Self::Mandatory) => Some(std::cmp::Ordering::Less), + (Self::Ordered { order: a }, Self::Ordered { order: b }) => b.partial_cmp(a), + } + } +} + +pub struct PromptChain { + args: PromptArguments, + templates: Vec<(PromptPriority, Box)>, +} + +impl PromptChain { + pub fn new( + args: PromptArguments, + templates: Vec<(PromptPriority, Box)>, + ) -> Self { + PromptChain { args, templates } + } + + pub fn generate(&self, truncate: bool) -> anyhow::Result<(String, usize)> { + // Argsort based on Prompt Priority + let seperator = "\n"; + let seperator_tokens = self.args.model.count_tokens(seperator)?; + let mut sorted_indices = (0..self.templates.len()).collect::>(); + sorted_indices.sort_by_key(|&i| Reverse(&self.templates[i].0)); + + // If Truncate + let mut tokens_outstanding = if truncate { + Some(self.args.model.capacity()? - self.args.reserved_tokens) + } else { + None + }; + + let mut prompts = vec!["".to_string(); sorted_indices.len()]; + for idx in sorted_indices { + let (_, template) = &self.templates[idx]; + + if let Some((template_prompt, prompt_token_count)) = + template.generate(&self.args, tokens_outstanding).log_err() + { + if template_prompt != "" { + prompts[idx] = template_prompt; + + if let Some(remaining_tokens) = tokens_outstanding { + let new_tokens = prompt_token_count + seperator_tokens; + tokens_outstanding = if remaining_tokens > new_tokens { + Some(remaining_tokens - new_tokens) + } else { + Some(0) + }; + } + } + } + } + + prompts.retain(|x| x != ""); + + let full_prompt = prompts.join(seperator); + let total_token_count = self.args.model.count_tokens(&full_prompt)?; + anyhow::Ok((prompts.join(seperator), total_token_count)) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use crate::models::TruncationDirection; + use crate::test::FakeLanguageModel; + + use super::*; + + #[test] + pub fn test_prompt_chain() { + struct TestPromptTemplate {} + impl PromptTemplate for TestPromptTemplate { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)> { + let mut content = "This is a test prompt template".to_string(); + + let mut token_count = args.model.count_tokens(&content)?; + if let Some(max_token_length) = max_token_length { + if token_count > max_token_length { + content = args.model.truncate( + &content, + max_token_length, + TruncationDirection::End, + )?; + token_count = max_token_length; + } + } + + anyhow::Ok((content, token_count)) + } + } + + struct TestLowPriorityTemplate {} + impl PromptTemplate for TestLowPriorityTemplate { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)> { + let mut content = "This is a low priority test prompt template".to_string(); + + let mut token_count = args.model.count_tokens(&content)?; + if let Some(max_token_length) = max_token_length { + if token_count > max_token_length { + content = args.model.truncate( + &content, + max_token_length, + TruncationDirection::End, + )?; + token_count = max_token_length; + } + } + + anyhow::Ok((content, token_count)) + } + } + + let model: Arc = Arc::new(FakeLanguageModel { capacity: 100 }); + let args = PromptArguments { + model: model.clone(), + language_name: None, + project_name: None, + snippets: Vec::new(), + reserved_tokens: 0, + buffer: None, + selected_range: None, + user_prompt: None, + }; + + let templates: Vec<(PromptPriority, Box)> = vec![ + ( + PromptPriority::Ordered { order: 0 }, + Box::new(TestPromptTemplate {}), + ), + ( + PromptPriority::Ordered { order: 1 }, + Box::new(TestLowPriorityTemplate {}), + ), + ]; + let chain = PromptChain::new(args, templates); + + let (prompt, token_count) = chain.generate(false).unwrap(); + + assert_eq!( + prompt, + "This is a test prompt template\nThis is a low priority test prompt template" + .to_string() + ); + + assert_eq!(model.count_tokens(&prompt).unwrap(), token_count); + + // Testing with Truncation Off + // Should ignore capacity and return all prompts + let model: Arc = Arc::new(FakeLanguageModel { capacity: 20 }); + let args = PromptArguments { + model: model.clone(), + language_name: None, + project_name: None, + snippets: Vec::new(), + reserved_tokens: 0, + buffer: None, + selected_range: None, + user_prompt: None, + }; + + let templates: Vec<(PromptPriority, Box)> = vec![ + ( + PromptPriority::Ordered { order: 0 }, + Box::new(TestPromptTemplate {}), + ), + ( + PromptPriority::Ordered { order: 1 }, + Box::new(TestLowPriorityTemplate {}), + ), + ]; + let chain = PromptChain::new(args, templates); + + let (prompt, token_count) = chain.generate(false).unwrap(); + + assert_eq!( + prompt, + "This is a test prompt template\nThis is a low priority test prompt template" + .to_string() + ); + + assert_eq!(model.count_tokens(&prompt).unwrap(), token_count); + + // Testing with Truncation Off + // Should ignore capacity and return all prompts + let capacity = 20; + let model: Arc = Arc::new(FakeLanguageModel { capacity }); + let args = PromptArguments { + model: model.clone(), + language_name: None, + project_name: None, + snippets: Vec::new(), + reserved_tokens: 0, + buffer: None, + selected_range: None, + user_prompt: None, + }; + + let templates: Vec<(PromptPriority, Box)> = vec![ + ( + PromptPriority::Ordered { order: 0 }, + Box::new(TestPromptTemplate {}), + ), + ( + PromptPriority::Ordered { order: 1 }, + Box::new(TestLowPriorityTemplate {}), + ), + ( + PromptPriority::Ordered { order: 2 }, + Box::new(TestLowPriorityTemplate {}), + ), + ]; + let chain = PromptChain::new(args, templates); + + let (prompt, token_count) = chain.generate(true).unwrap(); + + assert_eq!(prompt, "This is a test promp".to_string()); + assert_eq!(token_count, capacity); + + // Change Ordering of Prompts Based on Priority + let capacity = 120; + let reserved_tokens = 10; + let model: Arc = Arc::new(FakeLanguageModel { capacity }); + let args = PromptArguments { + model: model.clone(), + language_name: None, + project_name: None, + snippets: Vec::new(), + reserved_tokens, + buffer: None, + selected_range: None, + user_prompt: None, + }; + let templates: Vec<(PromptPriority, Box)> = vec![ + ( + PromptPriority::Mandatory, + Box::new(TestLowPriorityTemplate {}), + ), + ( + PromptPriority::Ordered { order: 0 }, + Box::new(TestPromptTemplate {}), + ), + ( + PromptPriority::Ordered { order: 1 }, + Box::new(TestLowPriorityTemplate {}), + ), + ]; + let chain = PromptChain::new(args, templates); + + let (prompt, token_count) = chain.generate(true).unwrap(); + + assert_eq!( + prompt, + "This is a low priority test prompt template\nThis is a test prompt template\nThis is a low priority test prompt " + .to_string() + ); + assert_eq!(token_count, capacity - reserved_tokens); + } +} diff --git a/crates/ai2/src/prompts/file_context.rs b/crates/ai2/src/prompts/file_context.rs new file mode 100644 index 0000000000..4a741beb24 --- /dev/null +++ b/crates/ai2/src/prompts/file_context.rs @@ -0,0 +1,164 @@ +use anyhow::anyhow; +use language2::BufferSnapshot; +use language2::ToOffset; + +use crate::models::LanguageModel; +use crate::models::TruncationDirection; +use crate::prompts::base::PromptArguments; +use crate::prompts::base::PromptTemplate; +use std::fmt::Write; +use std::ops::Range; +use std::sync::Arc; + +fn retrieve_context( + buffer: &BufferSnapshot, + selected_range: &Option>, + model: Arc, + max_token_count: Option, +) -> anyhow::Result<(String, usize, bool)> { + let mut prompt = String::new(); + let mut truncated = false; + if let Some(selected_range) = selected_range { + let start = selected_range.start.to_offset(buffer); + let end = selected_range.end.to_offset(buffer); + + let start_window = buffer.text_for_range(0..start).collect::(); + + let mut selected_window = String::new(); + if start == end { + write!(selected_window, "<|START|>").unwrap(); + } else { + write!(selected_window, "<|START|").unwrap(); + } + + write!( + selected_window, + "{}", + buffer.text_for_range(start..end).collect::() + ) + .unwrap(); + + if start != end { + write!(selected_window, "|END|>").unwrap(); + } + + let end_window = buffer.text_for_range(end..buffer.len()).collect::(); + + if let Some(max_token_count) = max_token_count { + let selected_tokens = model.count_tokens(&selected_window)?; + if selected_tokens > max_token_count { + return Err(anyhow!( + "selected range is greater than model context window, truncation not possible" + )); + }; + + let mut remaining_tokens = max_token_count - selected_tokens; + let start_window_tokens = model.count_tokens(&start_window)?; + let end_window_tokens = model.count_tokens(&end_window)?; + let outside_tokens = start_window_tokens + end_window_tokens; + if outside_tokens > remaining_tokens { + let (start_goal_tokens, end_goal_tokens) = + if start_window_tokens < end_window_tokens { + let start_goal_tokens = (remaining_tokens / 2).min(start_window_tokens); + remaining_tokens -= start_goal_tokens; + let end_goal_tokens = remaining_tokens.min(end_window_tokens); + (start_goal_tokens, end_goal_tokens) + } else { + let end_goal_tokens = (remaining_tokens / 2).min(end_window_tokens); + remaining_tokens -= end_goal_tokens; + let start_goal_tokens = remaining_tokens.min(start_window_tokens); + (start_goal_tokens, end_goal_tokens) + }; + + let truncated_start_window = + model.truncate(&start_window, start_goal_tokens, TruncationDirection::Start)?; + let truncated_end_window = + model.truncate(&end_window, end_goal_tokens, TruncationDirection::End)?; + writeln!( + prompt, + "{truncated_start_window}{selected_window}{truncated_end_window}" + ) + .unwrap(); + truncated = true; + } else { + writeln!(prompt, "{start_window}{selected_window}{end_window}").unwrap(); + } + } else { + // If we dont have a selected range, include entire file. + writeln!(prompt, "{}", &buffer.text()).unwrap(); + + // Dumb truncation strategy + if let Some(max_token_count) = max_token_count { + if model.count_tokens(&prompt)? > max_token_count { + truncated = true; + prompt = model.truncate(&prompt, max_token_count, TruncationDirection::End)?; + } + } + } + } + + let token_count = model.count_tokens(&prompt)?; + anyhow::Ok((prompt, token_count, truncated)) +} + +pub struct FileContext {} + +impl PromptTemplate for FileContext { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)> { + if let Some(buffer) = &args.buffer { + let mut prompt = String::new(); + // Add Initial Preamble + // TODO: Do we want to add the path in here? + writeln!( + prompt, + "The file you are currently working on has the following content:" + ) + .unwrap(); + + let language_name = args + .language_name + .clone() + .unwrap_or("".to_string()) + .to_lowercase(); + + let (context, _, truncated) = retrieve_context( + buffer, + &args.selected_range, + args.model.clone(), + max_token_length, + )?; + writeln!(prompt, "```{language_name}\n{context}\n```").unwrap(); + + if truncated { + writeln!(prompt, "Note the content has been truncated and only represents a portion of the file.").unwrap(); + } + + if let Some(selected_range) = &args.selected_range { + let start = selected_range.start.to_offset(buffer); + let end = selected_range.end.to_offset(buffer); + + if start == end { + writeln!(prompt, "In particular, the user's cursor is currently on the '<|START|>' span in the above content, with no text selected.").unwrap(); + } else { + writeln!(prompt, "In particular, the user has selected a section of the text between the '<|START|' and '|END|>' spans.").unwrap(); + } + } + + // Really dumb truncation strategy + if let Some(max_tokens) = max_token_length { + prompt = args + .model + .truncate(&prompt, max_tokens, TruncationDirection::End)?; + } + + let token_count = args.model.count_tokens(&prompt)?; + anyhow::Ok((prompt, token_count)) + } else { + Err(anyhow!("no buffer provided to retrieve file context from")) + } + } +} diff --git a/crates/ai2/src/prompts/generate.rs b/crates/ai2/src/prompts/generate.rs new file mode 100644 index 0000000000..c7be620107 --- /dev/null +++ b/crates/ai2/src/prompts/generate.rs @@ -0,0 +1,99 @@ +use crate::prompts::base::{PromptArguments, PromptFileType, PromptTemplate}; +use anyhow::anyhow; +use std::fmt::Write; + +pub fn capitalize(s: &str) -> String { + let mut c = s.chars(); + match c.next() { + None => String::new(), + Some(f) => f.to_uppercase().collect::() + c.as_str(), + } +} + +pub struct GenerateInlineContent {} + +impl PromptTemplate for GenerateInlineContent { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)> { + let Some(user_prompt) = &args.user_prompt else { + return Err(anyhow!("user prompt not provided")); + }; + + let file_type = args.get_file_type(); + let content_type = match &file_type { + PromptFileType::Code => "code", + PromptFileType::Text => "text", + }; + + let mut prompt = String::new(); + + if let Some(selected_range) = &args.selected_range { + if selected_range.start == selected_range.end { + writeln!( + prompt, + "Assume the cursor is located where the `<|START|>` span is." + ) + .unwrap(); + writeln!( + prompt, + "{} can't be replaced, so assume your answer will be inserted at the cursor.", + capitalize(content_type) + ) + .unwrap(); + writeln!( + prompt, + "Generate {content_type} based on the users prompt: {user_prompt}", + ) + .unwrap(); + } else { + writeln!(prompt, "Modify the user's selected {content_type} based upon the users prompt: '{user_prompt}'").unwrap(); + writeln!(prompt, "You must reply with only the adjusted {content_type} (within the '<|START|' and '|END|>' spans) not the entire file.").unwrap(); + writeln!(prompt, "Double check that you only return code and not the '<|START|' and '|END|'> spans").unwrap(); + } + } else { + writeln!( + prompt, + "Generate {content_type} based on the users prompt: {user_prompt}" + ) + .unwrap(); + } + + if let Some(language_name) = &args.language_name { + writeln!( + prompt, + "Your answer MUST always and only be valid {}.", + language_name + ) + .unwrap(); + } + writeln!(prompt, "Never make remarks about the output.").unwrap(); + writeln!( + prompt, + "Do not return anything else, except the generated {content_type}." + ) + .unwrap(); + + match file_type { + PromptFileType::Code => { + // writeln!(prompt, "Always wrap your code in a Markdown block.").unwrap(); + } + _ => {} + } + + // Really dumb truncation strategy + if let Some(max_tokens) = max_token_length { + prompt = args.model.truncate( + &prompt, + max_tokens, + crate::models::TruncationDirection::End, + )?; + } + + let token_count = args.model.count_tokens(&prompt)?; + + anyhow::Ok((prompt, token_count)) + } +} diff --git a/crates/ai2/src/prompts/mod.rs b/crates/ai2/src/prompts/mod.rs new file mode 100644 index 0000000000..0025269a44 --- /dev/null +++ b/crates/ai2/src/prompts/mod.rs @@ -0,0 +1,5 @@ +pub mod base; +pub mod file_context; +pub mod generate; +pub mod preamble; +pub mod repository_context; diff --git a/crates/ai2/src/prompts/preamble.rs b/crates/ai2/src/prompts/preamble.rs new file mode 100644 index 0000000000..92e0edeb78 --- /dev/null +++ b/crates/ai2/src/prompts/preamble.rs @@ -0,0 +1,52 @@ +use crate::prompts::base::{PromptArguments, PromptFileType, PromptTemplate}; +use std::fmt::Write; + +pub struct EngineerPreamble {} + +impl PromptTemplate for EngineerPreamble { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)> { + let mut prompts = Vec::new(); + + match args.get_file_type() { + PromptFileType::Code => { + prompts.push(format!( + "You are an expert {}engineer.", + args.language_name.clone().unwrap_or("".to_string()) + " " + )); + } + PromptFileType::Text => { + prompts.push("You are an expert engineer.".to_string()); + } + } + + if let Some(project_name) = args.project_name.clone() { + prompts.push(format!( + "You are currently working inside the '{project_name}' project in code editor Zed." + )); + } + + if let Some(mut remaining_tokens) = max_token_length { + let mut prompt = String::new(); + let mut total_count = 0; + for prompt_piece in prompts { + let prompt_token_count = + args.model.count_tokens(&prompt_piece)? + args.model.count_tokens("\n")?; + if remaining_tokens > prompt_token_count { + writeln!(prompt, "{prompt_piece}").unwrap(); + remaining_tokens -= prompt_token_count; + total_count += prompt_token_count; + } + } + + anyhow::Ok((prompt, total_count)) + } else { + let prompt = prompts.join("\n"); + let token_count = args.model.count_tokens(&prompt)?; + anyhow::Ok((prompt, token_count)) + } + } +} diff --git a/crates/ai2/src/prompts/repository_context.rs b/crates/ai2/src/prompts/repository_context.rs new file mode 100644 index 0000000000..78db5a1651 --- /dev/null +++ b/crates/ai2/src/prompts/repository_context.rs @@ -0,0 +1,98 @@ +use crate::prompts::base::{PromptArguments, PromptTemplate}; +use std::fmt::Write; +use std::{ops::Range, path::PathBuf}; + +use gpui2::{AsyncAppContext, Handle}; +use language2::{Anchor, Buffer}; + +#[derive(Clone)] +pub struct PromptCodeSnippet { + path: Option, + language_name: Option, + content: String, +} + +impl PromptCodeSnippet { + pub fn new( + buffer: Handle, + range: Range, + cx: &mut AsyncAppContext, + ) -> anyhow::Result { + let (content, language_name, file_path) = buffer.update(cx, |buffer, _| { + let snapshot = buffer.snapshot(); + let content = snapshot.text_for_range(range.clone()).collect::(); + + let language_name = buffer + .language() + .and_then(|language| Some(language.name().to_string().to_lowercase())); + + let file_path = buffer + .file() + .and_then(|file| Some(file.path().to_path_buf())); + + (content, language_name, file_path) + })?; + + anyhow::Ok(PromptCodeSnippet { + path: file_path, + language_name, + content, + }) + } +} + +impl ToString for PromptCodeSnippet { + fn to_string(&self) -> String { + let path = self + .path + .as_ref() + .and_then(|path| Some(path.to_string_lossy().to_string())) + .unwrap_or("".to_string()); + let language_name = self.language_name.clone().unwrap_or("".to_string()); + let content = self.content.clone(); + + format!("The below code snippet may be relevant from file: {path}\n```{language_name}\n{content}\n```") + } +} + +pub struct RepositoryContext {} + +impl PromptTemplate for RepositoryContext { + fn generate( + &self, + args: &PromptArguments, + max_token_length: Option, + ) -> anyhow::Result<(String, usize)> { + const MAXIMUM_SNIPPET_TOKEN_COUNT: usize = 500; + let template = "You are working inside a large repository, here are a few code snippets that may be useful."; + let mut prompt = String::new(); + + let mut remaining_tokens = max_token_length.clone(); + let seperator_token_length = args.model.count_tokens("\n")?; + for snippet in &args.snippets { + let mut snippet_prompt = template.to_string(); + let content = snippet.to_string(); + writeln!(snippet_prompt, "{content}").unwrap(); + + let token_count = args.model.count_tokens(&snippet_prompt)?; + if token_count <= MAXIMUM_SNIPPET_TOKEN_COUNT { + if let Some(tokens_left) = remaining_tokens { + if tokens_left >= token_count { + writeln!(prompt, "{snippet_prompt}").unwrap(); + remaining_tokens = if tokens_left >= (token_count + seperator_token_length) + { + Some(tokens_left - token_count - seperator_token_length) + } else { + Some(0) + }; + } + } else { + writeln!(prompt, "{snippet_prompt}").unwrap(); + } + } + } + + let total_token_count = args.model.count_tokens(&prompt)?; + anyhow::Ok((prompt, total_token_count)) + } +} diff --git a/crates/ai2/src/providers/mod.rs b/crates/ai2/src/providers/mod.rs new file mode 100644 index 0000000000..acd0f9d910 --- /dev/null +++ b/crates/ai2/src/providers/mod.rs @@ -0,0 +1 @@ +pub mod open_ai; diff --git a/crates/ai2/src/providers/open_ai/completion.rs b/crates/ai2/src/providers/open_ai/completion.rs new file mode 100644 index 0000000000..eca5611027 --- /dev/null +++ b/crates/ai2/src/providers/open_ai/completion.rs @@ -0,0 +1,306 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures::{ + future::BoxFuture, io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, FutureExt, + Stream, StreamExt, +}; +use gpui2::{AppContext, Executor}; +use isahc::{http::StatusCode, Request, RequestExt}; +use parking_lot::RwLock; +use serde::{Deserialize, Serialize}; +use std::{ + env, + fmt::{self, Display}, + io, + sync::Arc, +}; +use util::ResultExt; + +use crate::{ + auth::{CredentialProvider, ProviderCredential}, + completion::{CompletionProvider, CompletionRequest}, + models::LanguageModel, +}; + +use crate::providers::open_ai::{OpenAILanguageModel, OPENAI_API_URL}; + +#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum Role { + User, + Assistant, + System, +} + +impl Role { + pub fn cycle(&mut self) { + *self = match self { + Role::User => Role::Assistant, + Role::Assistant => Role::System, + Role::System => Role::User, + } + } +} + +impl Display for Role { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Role::User => write!(f, "User"), + Role::Assistant => write!(f, "Assistant"), + Role::System => write!(f, "System"), + } + } +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +pub struct RequestMessage { + pub role: Role, + pub content: String, +} + +#[derive(Debug, Default, Serialize)] +pub struct OpenAIRequest { + pub model: String, + pub messages: Vec, + pub stream: bool, + pub stop: Vec, + pub temperature: f32, +} + +impl CompletionRequest for OpenAIRequest { + fn data(&self) -> serde_json::Result { + serde_json::to_string(self) + } +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] +pub struct ResponseMessage { + pub role: Option, + pub content: Option, +} + +#[derive(Deserialize, Debug)] +pub struct OpenAIUsage { + pub prompt_tokens: u32, + pub completion_tokens: u32, + pub total_tokens: u32, +} + +#[derive(Deserialize, Debug)] +pub struct ChatChoiceDelta { + pub index: u32, + pub delta: ResponseMessage, + pub finish_reason: Option, +} + +#[derive(Deserialize, Debug)] +pub struct OpenAIResponseStreamEvent { + pub id: Option, + pub object: String, + pub created: u32, + pub model: String, + pub choices: Vec, + pub usage: Option, +} + +pub async fn stream_completion( + credential: ProviderCredential, + executor: Arc, + request: Box, +) -> Result>> { + let api_key = match credential { + ProviderCredential::Credentials { api_key } => api_key, + _ => { + return Err(anyhow!("no credentials provider for completion")); + } + }; + + let (tx, rx) = futures::channel::mpsc::unbounded::>(); + + let json_data = request.data()?; + let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions")) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", api_key)) + .body(json_data)? + .send_async() + .await?; + + let status = response.status(); + if status == StatusCode::OK { + executor + .spawn(async move { + let mut lines = BufReader::new(response.body_mut()).lines(); + + fn parse_line( + line: Result, + ) -> Result> { + if let Some(data) = line?.strip_prefix("data: ") { + let event = serde_json::from_str(&data)?; + Ok(Some(event)) + } else { + Ok(None) + } + } + + while let Some(line) = lines.next().await { + if let Some(event) = parse_line(line).transpose() { + let done = event.as_ref().map_or(false, |event| { + event + .choices + .last() + .map_or(false, |choice| choice.finish_reason.is_some()) + }); + if tx.unbounded_send(event).is_err() { + break; + } + + if done { + break; + } + } + } + + anyhow::Ok(()) + }) + .detach(); + + Ok(rx) + } else { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + #[derive(Deserialize)] + struct OpenAIResponse { + error: OpenAIError, + } + + #[derive(Deserialize)] + struct OpenAIError { + message: String, + } + + match serde_json::from_str::(&body) { + Ok(response) if !response.error.message.is_empty() => Err(anyhow!( + "Failed to connect to OpenAI API: {}", + response.error.message, + )), + + _ => Err(anyhow!( + "Failed to connect to OpenAI API: {} {}", + response.status(), + body, + )), + } + } +} + +#[derive(Clone)] +pub struct OpenAICompletionProvider { + model: OpenAILanguageModel, + credential: Arc>, + executor: Arc, +} + +impl OpenAICompletionProvider { + pub fn new(model_name: &str, executor: Arc) -> Self { + let model = OpenAILanguageModel::load(model_name); + let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials)); + Self { + model, + credential, + executor, + } + } +} + +#[async_trait] +impl CredentialProvider for OpenAICompletionProvider { + fn has_credentials(&self) -> bool { + match *self.credential.read() { + ProviderCredential::Credentials { .. } => true, + _ => false, + } + } + async fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential { + let existing_credential = self.credential.read().clone(); + + let retrieved_credential = cx + .run_on_main(move |cx| match existing_credential { + ProviderCredential::Credentials { .. } => { + return existing_credential.clone(); + } + _ => { + if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() { + return ProviderCredential::Credentials { api_key }; + } + + if let Some(Some((_, api_key))) = cx.read_credentials(OPENAI_API_URL).log_err() + { + if let Some(api_key) = String::from_utf8(api_key).log_err() { + return ProviderCredential::Credentials { api_key }; + } else { + return ProviderCredential::NoCredentials; + } + } else { + return ProviderCredential::NoCredentials; + } + } + }) + .await; + + *self.credential.write() = retrieved_credential.clone(); + retrieved_credential + } + + async fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential) { + *self.credential.write() = credential.clone(); + let credential = credential.clone(); + cx.run_on_main(move |cx| match credential { + ProviderCredential::Credentials { api_key } => { + cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) + .log_err(); + } + _ => {} + }) + .await; + } + async fn delete_credentials(&self, cx: &mut AppContext) { + cx.run_on_main(move |cx| cx.delete_credentials(OPENAI_API_URL).log_err()) + .await; + *self.credential.write() = ProviderCredential::NoCredentials; + } +} + +impl CompletionProvider for OpenAICompletionProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(self.model.clone()); + model + } + fn complete( + &self, + prompt: Box, + ) -> BoxFuture<'static, Result>>> { + // Currently the CompletionRequest for OpenAI, includes a 'model' parameter + // This means that the model is determined by the CompletionRequest and not the CompletionProvider, + // which is currently model based, due to the langauge model. + // At some point in the future we should rectify this. + let credential = self.credential.read().clone(); + let request = stream_completion(credential, self.executor.clone(), prompt); + async move { + let response = request.await?; + let stream = response + .filter_map(|response| async move { + match response { + Ok(mut response) => Some(Ok(response.choices.pop()?.delta.content?)), + Err(error) => Some(Err(error)), + } + }) + .boxed(); + Ok(stream) + } + .boxed() + } + fn box_clone(&self) -> Box { + Box::new((*self).clone()) + } +} diff --git a/crates/ai2/src/providers/open_ai/embedding.rs b/crates/ai2/src/providers/open_ai/embedding.rs new file mode 100644 index 0000000000..fc49c15134 --- /dev/null +++ b/crates/ai2/src/providers/open_ai/embedding.rs @@ -0,0 +1,313 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures::AsyncReadExt; +use gpui2::Executor; +use gpui2::{serde_json, AppContext}; +use isahc::http::StatusCode; +use isahc::prelude::Configurable; +use isahc::{AsyncBody, Response}; +use lazy_static::lazy_static; +use parking_lot::{Mutex, RwLock}; +use parse_duration::parse; +use postage::watch; +use serde::{Deserialize, Serialize}; +use std::env; +use std::ops::Add; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tiktoken_rs::{cl100k_base, CoreBPE}; +use util::http::{HttpClient, Request}; +use util::ResultExt; + +use crate::auth::{CredentialProvider, ProviderCredential}; +use crate::embedding::{Embedding, EmbeddingProvider}; +use crate::models::LanguageModel; +use crate::providers::open_ai::OpenAILanguageModel; + +use crate::providers::open_ai::OPENAI_API_URL; + +lazy_static! { + static ref OPENAI_BPE_TOKENIZER: CoreBPE = cl100k_base().unwrap(); +} + +#[derive(Clone)] +pub struct OpenAIEmbeddingProvider { + model: OpenAILanguageModel, + credential: Arc>, + pub client: Arc, + pub executor: Arc, + rate_limit_count_rx: watch::Receiver>, + rate_limit_count_tx: Arc>>>, +} + +#[derive(Serialize)] +struct OpenAIEmbeddingRequest<'a> { + model: &'static str, + input: Vec<&'a str>, +} + +#[derive(Deserialize)] +struct OpenAIEmbeddingResponse { + data: Vec, + usage: OpenAIEmbeddingUsage, +} + +#[derive(Debug, Deserialize)] +struct OpenAIEmbedding { + embedding: Vec, + index: usize, + object: String, +} + +#[derive(Deserialize)] +struct OpenAIEmbeddingUsage { + prompt_tokens: usize, + total_tokens: usize, +} + +impl OpenAIEmbeddingProvider { + pub fn new(client: Arc, executor: Arc) -> Self { + let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None); + let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx)); + + let model = OpenAILanguageModel::load("text-embedding-ada-002"); + let credential = Arc::new(RwLock::new(ProviderCredential::NoCredentials)); + + OpenAIEmbeddingProvider { + model, + credential, + client, + executor, + rate_limit_count_rx, + rate_limit_count_tx, + } + } + + fn get_api_key(&self) -> Result { + match self.credential.read().clone() { + ProviderCredential::Credentials { api_key } => Ok(api_key), + _ => Err(anyhow!("api credentials not provided")), + } + } + + fn resolve_rate_limit(&self) { + let reset_time = *self.rate_limit_count_tx.lock().borrow(); + + if let Some(reset_time) = reset_time { + if Instant::now() >= reset_time { + *self.rate_limit_count_tx.lock().borrow_mut() = None + } + } + + log::trace!( + "resolving reset time: {:?}", + *self.rate_limit_count_tx.lock().borrow() + ); + } + + fn update_reset_time(&self, reset_time: Instant) { + let original_time = *self.rate_limit_count_tx.lock().borrow(); + + let updated_time = if let Some(original_time) = original_time { + if reset_time < original_time { + Some(reset_time) + } else { + Some(original_time) + } + } else { + Some(reset_time) + }; + + log::trace!("updating rate limit time: {:?}", updated_time); + + *self.rate_limit_count_tx.lock().borrow_mut() = updated_time; + } + async fn send_request( + &self, + api_key: &str, + spans: Vec<&str>, + request_timeout: u64, + ) -> Result> { + let request = Request::post("https://api.openai.com/v1/embeddings") + .redirect_policy(isahc::config::RedirectPolicy::Follow) + .timeout(Duration::from_secs(request_timeout)) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", api_key)) + .body( + serde_json::to_string(&OpenAIEmbeddingRequest { + input: spans.clone(), + model: "text-embedding-ada-002", + }) + .unwrap() + .into(), + )?; + + Ok(self.client.send(request).await?) + } +} + +#[async_trait] +impl CredentialProvider for OpenAIEmbeddingProvider { + fn has_credentials(&self) -> bool { + match *self.credential.read() { + ProviderCredential::Credentials { .. } => true, + _ => false, + } + } + async fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential { + let existing_credential = self.credential.read().clone(); + + let retrieved_credential = cx + .run_on_main(move |cx| match existing_credential { + ProviderCredential::Credentials { .. } => { + return existing_credential.clone(); + } + _ => { + if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() { + return ProviderCredential::Credentials { api_key }; + } + + if let Some(Some((_, api_key))) = cx.read_credentials(OPENAI_API_URL).log_err() + { + if let Some(api_key) = String::from_utf8(api_key).log_err() { + return ProviderCredential::Credentials { api_key }; + } else { + return ProviderCredential::NoCredentials; + } + } else { + return ProviderCredential::NoCredentials; + } + } + }) + .await; + + *self.credential.write() = retrieved_credential.clone(); + retrieved_credential + } + + async fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential) { + *self.credential.write() = credential.clone(); + let credential = credential.clone(); + cx.run_on_main(move |cx| match credential { + ProviderCredential::Credentials { api_key } => { + cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes()) + .log_err(); + } + _ => {} + }) + .await; + } + async fn delete_credentials(&self, cx: &mut AppContext) { + cx.run_on_main(move |cx| cx.delete_credentials(OPENAI_API_URL).log_err()) + .await; + *self.credential.write() = ProviderCredential::NoCredentials; + } +} + +#[async_trait] +impl EmbeddingProvider for OpenAIEmbeddingProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(self.model.clone()); + model + } + + fn max_tokens_per_batch(&self) -> usize { + 50000 + } + + fn rate_limit_expiration(&self) -> Option { + *self.rate_limit_count_rx.borrow() + } + + async fn embed_batch(&self, spans: Vec) -> Result> { + const BACKOFF_SECONDS: [usize; 4] = [3, 5, 15, 45]; + const MAX_RETRIES: usize = 4; + + let api_key = self.get_api_key()?; + + let mut request_number = 0; + let mut rate_limiting = false; + let mut request_timeout: u64 = 15; + let mut response: Response; + while request_number < MAX_RETRIES { + response = self + .send_request( + &api_key, + spans.iter().map(|x| &**x).collect(), + request_timeout, + ) + .await?; + + request_number += 1; + + match response.status() { + StatusCode::REQUEST_TIMEOUT => { + request_timeout += 5; + } + StatusCode::OK => { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + let response: OpenAIEmbeddingResponse = serde_json::from_str(&body)?; + + log::trace!( + "openai embedding completed. tokens: {:?}", + response.usage.total_tokens + ); + + // If we complete a request successfully that was previously rate_limited + // resolve the rate limit + if rate_limiting { + self.resolve_rate_limit() + } + + return Ok(response + .data + .into_iter() + .map(|embedding| Embedding::from(embedding.embedding)) + .collect()); + } + StatusCode::TOO_MANY_REQUESTS => { + rate_limiting = true; + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + let delay_duration = { + let delay = Duration::from_secs(BACKOFF_SECONDS[request_number - 1] as u64); + if let Some(time_to_reset) = + response.headers().get("x-ratelimit-reset-tokens") + { + if let Ok(time_str) = time_to_reset.to_str() { + parse(time_str).unwrap_or(delay) + } else { + delay + } + } else { + delay + } + }; + + // If we've previously rate limited, increment the duration but not the count + let reset_time = Instant::now().add(delay_duration); + self.update_reset_time(reset_time); + + log::trace!( + "openai rate limiting: waiting {:?} until lifted", + &delay_duration + ); + + self.executor.timer(delay_duration).await; + } + _ => { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + return Err(anyhow!( + "open ai bad request: {:?} {:?}", + &response.status(), + body + )); + } + } + } + Err(anyhow!("openai max retries")) + } +} diff --git a/crates/ai2/src/providers/open_ai/mod.rs b/crates/ai2/src/providers/open_ai/mod.rs new file mode 100644 index 0000000000..7d2f86045d --- /dev/null +++ b/crates/ai2/src/providers/open_ai/mod.rs @@ -0,0 +1,9 @@ +pub mod completion; +pub mod embedding; +pub mod model; + +pub use completion::*; +pub use embedding::*; +pub use model::OpenAILanguageModel; + +pub const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; diff --git a/crates/ai2/src/providers/open_ai/model.rs b/crates/ai2/src/providers/open_ai/model.rs new file mode 100644 index 0000000000..6e306c80b9 --- /dev/null +++ b/crates/ai2/src/providers/open_ai/model.rs @@ -0,0 +1,57 @@ +use anyhow::anyhow; +use tiktoken_rs::CoreBPE; +use util::ResultExt; + +use crate::models::{LanguageModel, TruncationDirection}; + +#[derive(Clone)] +pub struct OpenAILanguageModel { + name: String, + bpe: Option, +} + +impl OpenAILanguageModel { + pub fn load(model_name: &str) -> Self { + let bpe = tiktoken_rs::get_bpe_from_model(model_name).log_err(); + OpenAILanguageModel { + name: model_name.to_string(), + bpe, + } + } +} + +impl LanguageModel for OpenAILanguageModel { + fn name(&self) -> String { + self.name.clone() + } + fn count_tokens(&self, content: &str) -> anyhow::Result { + if let Some(bpe) = &self.bpe { + anyhow::Ok(bpe.encode_with_special_tokens(content).len()) + } else { + Err(anyhow!("bpe for open ai model was not retrieved")) + } + } + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result { + if let Some(bpe) = &self.bpe { + let tokens = bpe.encode_with_special_tokens(content); + if tokens.len() > length { + match direction { + TruncationDirection::End => bpe.decode(tokens[..length].to_vec()), + TruncationDirection::Start => bpe.decode(tokens[length..].to_vec()), + } + } else { + bpe.decode(tokens) + } + } else { + Err(anyhow!("bpe for open ai model was not retrieved")) + } + } + fn capacity(&self) -> anyhow::Result { + anyhow::Ok(tiktoken_rs::model::get_context_size(&self.name)) + } +} diff --git a/crates/ai2/src/providers/open_ai/new.rs b/crates/ai2/src/providers/open_ai/new.rs new file mode 100644 index 0000000000..c7d67f2ba1 --- /dev/null +++ b/crates/ai2/src/providers/open_ai/new.rs @@ -0,0 +1,11 @@ +pub trait LanguageModel { + fn name(&self) -> String; + fn count_tokens(&self, content: &str) -> anyhow::Result; + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result; + fn capacity(&self) -> anyhow::Result; +} diff --git a/crates/ai2/src/test.rs b/crates/ai2/src/test.rs new file mode 100644 index 0000000000..ee88529aec --- /dev/null +++ b/crates/ai2/src/test.rs @@ -0,0 +1,193 @@ +use std::{ + sync::atomic::{self, AtomicUsize, Ordering}, + time::Instant, +}; + +use async_trait::async_trait; +use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt}; +use gpui2::AppContext; +use parking_lot::Mutex; + +use crate::{ + auth::{CredentialProvider, ProviderCredential}, + completion::{CompletionProvider, CompletionRequest}, + embedding::{Embedding, EmbeddingProvider}, + models::{LanguageModel, TruncationDirection}, +}; + +#[derive(Clone)] +pub struct FakeLanguageModel { + pub capacity: usize, +} + +impl LanguageModel for FakeLanguageModel { + fn name(&self) -> String { + "dummy".to_string() + } + fn count_tokens(&self, content: &str) -> anyhow::Result { + anyhow::Ok(content.chars().collect::>().len()) + } + fn truncate( + &self, + content: &str, + length: usize, + direction: TruncationDirection, + ) -> anyhow::Result { + println!("TRYING TO TRUNCATE: {:?}", length.clone()); + + if length > self.count_tokens(content)? { + println!("NOT TRUNCATING"); + return anyhow::Ok(content.to_string()); + } + + anyhow::Ok(match direction { + TruncationDirection::End => content.chars().collect::>()[..length] + .into_iter() + .collect::(), + TruncationDirection::Start => content.chars().collect::>()[length..] + .into_iter() + .collect::(), + }) + } + fn capacity(&self) -> anyhow::Result { + anyhow::Ok(self.capacity) + } +} + +pub struct FakeEmbeddingProvider { + pub embedding_count: AtomicUsize, +} + +impl Clone for FakeEmbeddingProvider { + fn clone(&self) -> Self { + FakeEmbeddingProvider { + embedding_count: AtomicUsize::new(self.embedding_count.load(Ordering::SeqCst)), + } + } +} + +impl Default for FakeEmbeddingProvider { + fn default() -> Self { + FakeEmbeddingProvider { + embedding_count: AtomicUsize::default(), + } + } +} + +impl FakeEmbeddingProvider { + pub fn embedding_count(&self) -> usize { + self.embedding_count.load(atomic::Ordering::SeqCst) + } + + pub fn embed_sync(&self, span: &str) -> Embedding { + let mut result = vec![1.0; 26]; + for letter in span.chars() { + let letter = letter.to_ascii_lowercase(); + if letter as u32 >= 'a' as u32 { + let ix = (letter as u32) - ('a' as u32); + if ix < 26 { + result[ix as usize] += 1.0; + } + } + } + + let norm = result.iter().map(|x| x * x).sum::().sqrt(); + for x in &mut result { + *x /= norm; + } + + result.into() + } +} + +#[async_trait] +impl CredentialProvider for FakeEmbeddingProvider { + fn has_credentials(&self) -> bool { + true + } + async fn retrieve_credentials(&self, _cx: &mut AppContext) -> ProviderCredential { + ProviderCredential::NotNeeded + } + async fn save_credentials(&self, _cx: &mut AppContext, _credential: ProviderCredential) {} + async fn delete_credentials(&self, _cx: &mut AppContext) {} +} + +#[async_trait] +impl EmbeddingProvider for FakeEmbeddingProvider { + fn base_model(&self) -> Box { + Box::new(FakeLanguageModel { capacity: 1000 }) + } + fn max_tokens_per_batch(&self) -> usize { + 1000 + } + + fn rate_limit_expiration(&self) -> Option { + None + } + + async fn embed_batch(&self, spans: Vec) -> anyhow::Result> { + self.embedding_count + .fetch_add(spans.len(), atomic::Ordering::SeqCst); + + anyhow::Ok(spans.iter().map(|span| self.embed_sync(span)).collect()) + } +} + +pub struct FakeCompletionProvider { + last_completion_tx: Mutex>>, +} + +impl Clone for FakeCompletionProvider { + fn clone(&self) -> Self { + Self { + last_completion_tx: Mutex::new(None), + } + } +} + +impl FakeCompletionProvider { + pub fn new() -> Self { + Self { + last_completion_tx: Mutex::new(None), + } + } + + pub fn send_completion(&self, completion: impl Into) { + let mut tx = self.last_completion_tx.lock(); + tx.as_mut().unwrap().try_send(completion.into()).unwrap(); + } + + pub fn finish_completion(&self) { + self.last_completion_tx.lock().take().unwrap(); + } +} + +#[async_trait] +impl CredentialProvider for FakeCompletionProvider { + fn has_credentials(&self) -> bool { + true + } + async fn retrieve_credentials(&self, _cx: &mut AppContext) -> ProviderCredential { + ProviderCredential::NotNeeded + } + async fn save_credentials(&self, _cx: &mut AppContext, _credential: ProviderCredential) {} + async fn delete_credentials(&self, _cx: &mut AppContext) {} +} + +impl CompletionProvider for FakeCompletionProvider { + fn base_model(&self) -> Box { + let model: Box = Box::new(FakeLanguageModel { capacity: 8190 }); + model + } + fn complete( + &self, + _prompt: Box, + ) -> BoxFuture<'static, anyhow::Result>>> { + let (tx, rx) = mpsc::channel(1); + *self.last_completion_tx.lock() = Some(tx); + async move { Ok(rx.map(|rx| Ok(rx)).boxed()) }.boxed() + } + fn box_clone(&self) -> Box { + Box::new((*self).clone()) + } +} diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index a6b31871dd..9f681a49e9 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -15,6 +15,7 @@ name = "Zed" path = "src/main.rs" [dependencies] +ai2 = { path = "../ai2"} # audio = { path = "../audio" } # activity_indicator = { path = "../activity_indicator" } # auto_update = { path = "../auto_update" } From 1a54ac0d69421a7adbc74131d60effcdea30c6f9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 30 Oct 2023 19:44:01 +0100 Subject: [PATCH 35/54] Rename Handle to Model --- crates/call2/src/call2.rs | 40 +- crates/call2/src/room.rs | 38 +- crates/client2/src/client2.rs | 28 +- crates/client2/src/test.rs | 6 +- crates/client2/src/user.rs | 10 +- crates/copilot2/src/copilot2.rs | 24 +- crates/gpui2/src/app.rs | 14 +- crates/gpui2/src/app/async_context.rs | 30 +- crates/gpui2/src/app/entity_map.rs | 52 +- crates/gpui2/src/app/model_context.rs | 30 +- crates/gpui2/src/app/test_context.rs | 16 +- crates/gpui2/src/gpui2.rs | 42 +- crates/gpui2/src/view.rs | 7 +- crates/gpui2/src/window.rs | 48 +- crates/language2/src/buffer_tests.rs | 73 +- crates/prettier2/src/prettier2.rs | 4 +- crates/project2/src/lsp_command.rs | 158 +- crates/project2/src/project2.rs | 278 +- crates/project2/src/terminals.rs | 8 +- crates/project2/src/worktree.rs | 24 +- crates/storybook2/src/stories/kitchen_sink.rs | 2 +- crates/ui2/src/components/buffer_search.rs | 2 +- crates/ui2/src/components/editor_pane.rs | 2 +- crates/ui2/src/components/title_bar.rs | 4 +- crates/ui2/src/components/workspace.rs | 13 +- crates/workspace2/src/item.rs | 1096 ++++ crates/workspace2/src/pane.rs | 2754 ++++++++ crates/workspace2/src/pane_group.rs | 993 +++ crates/workspace2/src/persistence/model.rs | 340 + crates/workspace2/src/workspace2.rs | 5535 +++++++++++++++++ crates/zed2/src/main.rs | 2 +- crates/zed2/src/zed2.rs | 4 +- 32 files changed, 11195 insertions(+), 482 deletions(-) create mode 100644 crates/workspace2/src/item.rs create mode 100644 crates/workspace2/src/pane.rs create mode 100644 crates/workspace2/src/pane_group.rs create mode 100644 crates/workspace2/src/persistence/model.rs create mode 100644 crates/workspace2/src/workspace2.rs diff --git a/crates/call2/src/call2.rs b/crates/call2/src/call2.rs index 1a514164ba..ffa2e5e9dc 100644 --- a/crates/call2/src/call2.rs +++ b/crates/call2/src/call2.rs @@ -12,7 +12,7 @@ use client2::{ use collections::HashSet; use futures::{future::Shared, FutureExt}; use gpui2::{ - AppContext, AsyncAppContext, Context, EventEmitter, Handle, ModelContext, Subscription, Task, + AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task, WeakHandle, }; use postage::watch; @@ -23,10 +23,10 @@ use std::sync::Arc; pub use participant::ParticipantLocation; pub use room::Room; -pub fn init(client: Arc, user_store: Handle, cx: &mut AppContext) { +pub fn init(client: Arc, user_store: Model, cx: &mut AppContext) { CallSettings::register(cx); - let active_call = cx.entity(|cx| ActiveCall::new(client, user_store, cx)); + let active_call = cx.build_model(|cx| ActiveCall::new(client, user_store, cx)); cx.set_global(active_call); } @@ -40,8 +40,8 @@ pub struct IncomingCall { /// Singleton global maintaining the user's participation in a room across workspaces. pub struct ActiveCall { - room: Option<(Handle, Vec)>, - pending_room_creation: Option, Arc>>>>, + room: Option<(Model, Vec)>, + pending_room_creation: Option, Arc>>>>, location: Option>, pending_invites: HashSet, incoming_call: ( @@ -49,7 +49,7 @@ pub struct ActiveCall { watch::Receiver>, ), client: Arc, - user_store: Handle, + user_store: Model, _subscriptions: Vec, } @@ -58,11 +58,7 @@ impl EventEmitter for ActiveCall { } impl ActiveCall { - fn new( - client: Arc, - user_store: Handle, - cx: &mut ModelContext, - ) -> Self { + fn new(client: Arc, user_store: Model, cx: &mut ModelContext) -> Self { Self { room: None, pending_room_creation: None, @@ -84,7 +80,7 @@ impl ActiveCall { } async fn handle_incoming_call( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -112,7 +108,7 @@ impl ActiveCall { } async fn handle_call_canceled( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -129,14 +125,14 @@ impl ActiveCall { Ok(()) } - pub fn global(cx: &AppContext) -> Handle { - cx.global::>().clone() + pub fn global(cx: &AppContext) -> Model { + cx.global::>().clone() } pub fn invite( &mut self, called_user_id: u64, - initial_project: Option>, + initial_project: Option>, cx: &mut ModelContext, ) -> Task> { if !self.pending_invites.insert(called_user_id) { @@ -291,7 +287,7 @@ impl ActiveCall { &mut self, channel_id: u64, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { if let Some(room) = self.room().cloned() { if room.read(cx).channel_id() == Some(channel_id) { return Task::ready(Ok(room)); @@ -327,7 +323,7 @@ impl ActiveCall { pub fn share_project( &mut self, - project: Handle, + project: Model, cx: &mut ModelContext, ) -> Task> { if let Some((room, _)) = self.room.as_ref() { @@ -340,7 +336,7 @@ impl ActiveCall { pub fn unshare_project( &mut self, - project: Handle, + project: Model, cx: &mut ModelContext, ) -> Result<()> { if let Some((room, _)) = self.room.as_ref() { @@ -357,7 +353,7 @@ impl ActiveCall { pub fn set_location( &mut self, - project: Option<&Handle>, + project: Option<&Model>, cx: &mut ModelContext, ) -> Task> { if project.is_some() || !*ZED_ALWAYS_ACTIVE { @@ -371,7 +367,7 @@ impl ActiveCall { fn set_room( &mut self, - room: Option>, + room: Option>, cx: &mut ModelContext, ) -> Task> { if room.as_ref() != self.room.as_ref().map(|room| &room.0) { @@ -407,7 +403,7 @@ impl ActiveCall { } } - pub fn room(&self) -> Option<&Handle> { + pub fn room(&self) -> Option<&Model> { self.room.as_ref().map(|(room, _)| room) } diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index 639191123f..07873c4cd5 100644 --- a/crates/call2/src/room.rs +++ b/crates/call2/src/room.rs @@ -16,7 +16,7 @@ use collections::{BTreeMap, HashMap, HashSet}; use fs::Fs; use futures::{FutureExt, StreamExt}; use gpui2::{ - AppContext, AsyncAppContext, Context, EventEmitter, Handle, ModelContext, Task, WeakHandle, + AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakHandle, }; use language2::LanguageRegistry; use live_kit_client::{LocalTrackPublication, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate}; @@ -70,7 +70,7 @@ pub struct Room { pending_call_count: usize, leave_when_empty: bool, client: Arc, - user_store: Handle, + user_store: Model, follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec>, client_subscriptions: Vec, _subscriptions: Vec, @@ -111,7 +111,7 @@ impl Room { channel_id: Option, live_kit_connection_info: Option, client: Arc, - user_store: Handle, + user_store: Model, cx: &mut ModelContext, ) -> Self { todo!() @@ -237,15 +237,15 @@ impl Room { pub(crate) fn create( called_user_id: u64, - initial_project: Option>, + initial_project: Option>, client: Arc, - user_store: Handle, + user_store: Model, cx: &mut AppContext, - ) -> Task>> { + ) -> Task>> { cx.spawn(move |mut cx| async move { let response = client.request(proto::CreateRoom {}).await?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - let room = cx.entity(|cx| { + let room = cx.build_model(|cx| { Self::new( room_proto.id, None, @@ -283,9 +283,9 @@ impl Room { pub(crate) fn join_channel( channel_id: u64, client: Arc, - user_store: Handle, + user_store: Model, cx: &mut AppContext, - ) -> Task>> { + ) -> Task>> { cx.spawn(move |cx| async move { Self::from_join_response( client.request(proto::JoinChannel { channel_id }).await?, @@ -299,9 +299,9 @@ impl Room { pub(crate) fn join( call: &IncomingCall, client: Arc, - user_store: Handle, + user_store: Model, cx: &mut AppContext, - ) -> Task>> { + ) -> Task>> { let id = call.room_id; cx.spawn(move |cx| async move { Self::from_join_response( @@ -343,11 +343,11 @@ impl Room { fn from_join_response( response: proto::JoinRoomResponse, client: Arc, - user_store: Handle, + user_store: Model, mut cx: AsyncAppContext, - ) -> Result> { + ) -> Result> { let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - let room = cx.entity(|cx| { + let room = cx.build_model(|cx| { Self::new( room_proto.id, response.channel_id, @@ -661,7 +661,7 @@ impl Room { } async fn handle_room_updated( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -1101,7 +1101,7 @@ impl Room { language_registry: Arc, fs: Arc, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let client = self.client.clone(); let user_store = self.user_store.clone(); cx.emit(Event::RemoteProjectJoined { project_id: id }); @@ -1125,7 +1125,7 @@ impl Room { pub(crate) fn share_project( &mut self, - project: Handle, + project: Model, cx: &mut ModelContext, ) -> Task> { if let Some(project_id) = project.read(cx).remote_id() { @@ -1161,7 +1161,7 @@ impl Room { pub(crate) fn unshare_project( &mut self, - project: Handle, + project: Model, cx: &mut ModelContext, ) -> Result<()> { let project_id = match project.read(cx).remote_id() { @@ -1175,7 +1175,7 @@ impl Room { pub(crate) fn set_location( &mut self, - project: Option<&Handle>, + project: Option<&Model>, cx: &mut ModelContext, ) -> Task> { if self.status.is_offline() { diff --git a/crates/client2/src/client2.rs b/crates/client2/src/client2.rs index 8eaf248521..dcea6ded4e 100644 --- a/crates/client2/src/client2.rs +++ b/crates/client2/src/client2.rs @@ -14,7 +14,7 @@ use futures::{ future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, TryStreamExt, }; use gpui2::{ - serde_json, AnyHandle, AnyWeakHandle, AppContext, AsyncAppContext, Handle, SemanticVersion, + serde_json, AnyHandle, AnyWeakHandle, AppContext, AsyncAppContext, Model, SemanticVersion, Task, WeakHandle, }; use lazy_static::lazy_static; @@ -314,7 +314,7 @@ impl PendingEntitySubscription where T: 'static + Send, { - pub fn set_model(mut self, model: &Handle, cx: &mut AsyncAppContext) -> Subscription { + pub fn set_model(mut self, model: &Model, cx: &mut AsyncAppContext) -> Subscription { self.consumed = true; let mut state = self.client.state.write(); let id = (TypeId::of::(), self.remote_id); @@ -558,7 +558,7 @@ impl Client { where M: EnvelopedMessage, E: 'static + Send, - H: 'static + Send + Sync + Fn(Handle, TypedEnvelope, Arc, AsyncAppContext) -> F, + H: 'static + Send + Sync + Fn(Model, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future> + Send, { let message_type_id = TypeId::of::(); @@ -600,7 +600,7 @@ impl Client { where M: RequestMessage, E: 'static + Send, - H: 'static + Send + Sync + Fn(Handle, TypedEnvelope, Arc, AsyncAppContext) -> F, + H: 'static + Send + Sync + Fn(Model, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future> + Send, { self.add_message_handler(model, move |handle, envelope, this, cx| { @@ -616,7 +616,7 @@ impl Client { where M: EntityMessage, E: 'static + Send, - H: 'static + Send + Sync + Fn(Handle, TypedEnvelope, Arc, AsyncAppContext) -> F, + H: 'static + Send + Sync + Fn(Model, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future> + Send, { self.add_entity_message_handler::(move |subscriber, message, client, cx| { @@ -667,7 +667,7 @@ impl Client { where M: EntityMessage + RequestMessage, E: 'static + Send, - H: 'static + Send + Sync + Fn(Handle, TypedEnvelope, Arc, AsyncAppContext) -> F, + H: 'static + Send + Sync + Fn(Model, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future> + Send, { self.add_model_message_handler(move |entity, envelope, client, cx| { @@ -1546,7 +1546,7 @@ mod tests { let (done_tx1, mut done_rx1) = smol::channel::unbounded(); let (done_tx2, mut done_rx2) = smol::channel::unbounded(); client.add_model_message_handler( - move |model: Handle, _: TypedEnvelope, _, mut cx| { + move |model: Model, _: TypedEnvelope, _, mut cx| { match model.update(&mut cx, |model, _| model.id).unwrap() { 1 => done_tx1.try_send(()).unwrap(), 2 => done_tx2.try_send(()).unwrap(), @@ -1555,15 +1555,15 @@ mod tests { async { Ok(()) } }, ); - let model1 = cx.entity(|_| Model { + let model1 = cx.build_model(|_| TestModel { id: 1, subscription: None, }); - let model2 = cx.entity(|_| Model { + let model2 = cx.build_model(|_| TestModel { id: 2, subscription: None, }); - let model3 = cx.entity(|_| Model { + let model3 = cx.build_model(|_| TestModel { id: 3, subscription: None, }); @@ -1596,7 +1596,7 @@ mod tests { let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); let server = FakeServer::for_client(user_id, &client, cx).await; - let model = cx.entity(|_| Model::default()); + let model = cx.build_model(|_| TestModel::default()); let (done_tx1, _done_rx1) = smol::channel::unbounded(); let (done_tx2, mut done_rx2) = smol::channel::unbounded(); let subscription1 = client.add_message_handler( @@ -1624,11 +1624,11 @@ mod tests { let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); let server = FakeServer::for_client(user_id, &client, cx).await; - let model = cx.entity(|_| Model::default()); + let model = cx.build_model(|_| TestModel::default()); let (done_tx, mut done_rx) = smol::channel::unbounded(); let subscription = client.add_message_handler( model.clone().downgrade(), - move |model: Handle, _: TypedEnvelope, _, mut cx| { + move |model: Model, _: TypedEnvelope, _, mut cx| { model .update(&mut cx, |model, _| model.subscription.take()) .unwrap(); @@ -1644,7 +1644,7 @@ mod tests { } #[derive(Default)] - struct Model { + struct TestModel { id: usize, subscription: Option, } diff --git a/crates/client2/src/test.rs b/crates/client2/src/test.rs index 1b32d35092..f30547dcfc 100644 --- a/crates/client2/src/test.rs +++ b/crates/client2/src/test.rs @@ -1,7 +1,7 @@ use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore}; use anyhow::{anyhow, Result}; use futures::{stream::BoxStream, StreamExt}; -use gpui2::{Context, Executor, Handle, TestAppContext}; +use gpui2::{Context, Executor, Model, TestAppContext}; use parking_lot::Mutex; use rpc2::{ proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse}, @@ -194,9 +194,9 @@ impl FakeServer { &self, client: Arc, cx: &mut TestAppContext, - ) -> Handle { + ) -> Model { let http_client = FakeHttpClient::with_404_response(); - let user_store = cx.entity(|cx| UserStore::new(client, http_client, cx)); + let user_store = cx.build_model(|cx| UserStore::new(client, http_client, cx)); assert_eq!( self.receive::() .await diff --git a/crates/client2/src/user.rs b/crates/client2/src/user.rs index 41cf46ea8f..a8be4b6401 100644 --- a/crates/client2/src/user.rs +++ b/crates/client2/src/user.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, HashMap, HashSet}; use feature_flags2::FeatureFlagAppExt; use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; -use gpui2::{AsyncAppContext, EventEmitter, Handle, ImageData, ModelContext, Task}; +use gpui2::{AsyncAppContext, EventEmitter, ImageData, Model, ModelContext, Task}; use postage::{sink::Sink, watch}; use rpc2::proto::{RequestMessage, UsersResponse}; use std::sync::{Arc, Weak}; @@ -213,7 +213,7 @@ impl UserStore { } async fn handle_update_invite_info( - this: Handle, + this: Model, message: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -229,7 +229,7 @@ impl UserStore { } async fn handle_show_contacts( - this: Handle, + this: Model, _: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -243,7 +243,7 @@ impl UserStore { } async fn handle_update_contacts( - this: Handle, + this: Model, message: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -690,7 +690,7 @@ impl User { impl Contact { async fn from_proto( contact: proto::Contact, - user_store: &Handle, + user_store: &Model, cx: &mut AsyncAppContext, ) -> Result { let user = user_store diff --git a/crates/copilot2/src/copilot2.rs b/crates/copilot2/src/copilot2.rs index 149b01aa82..42b0e3aa41 100644 --- a/crates/copilot2/src/copilot2.rs +++ b/crates/copilot2/src/copilot2.rs @@ -7,7 +7,7 @@ use async_tar::Archive; use collections::{HashMap, HashSet}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use gpui2::{ - AppContext, AsyncAppContext, Context, EntityId, EventEmitter, Handle, ModelContext, Task, + AppContext, AsyncAppContext, Context, EntityId, EventEmitter, Model, ModelContext, Task, WeakHandle, }; use language2::{ @@ -49,7 +49,7 @@ pub fn init( node_runtime: Arc, cx: &mut AppContext, ) { - let copilot = cx.entity({ + let copilot = cx.build_model({ let node_runtime = node_runtime.clone(); move |cx| Copilot::start(new_server_id, http, node_runtime, cx) }); @@ -183,7 +183,7 @@ struct RegisteredBuffer { impl RegisteredBuffer { fn report_changes( &mut self, - buffer: &Handle, + buffer: &Model, cx: &mut ModelContext, ) -> oneshot::Receiver<(i32, BufferSnapshot)> { let (done_tx, done_rx) = oneshot::channel(); @@ -292,9 +292,9 @@ impl EventEmitter for Copilot { } impl Copilot { - pub fn global(cx: &AppContext) -> Option> { - if cx.has_global::>() { - Some(cx.global::>().clone()) + pub fn global(cx: &AppContext) -> Option> { + if cx.has_global::>() { + Some(cx.global::>().clone()) } else { None } @@ -590,7 +590,7 @@ impl Copilot { } } - pub fn register_buffer(&mut self, buffer: &Handle, cx: &mut ModelContext) { + pub fn register_buffer(&mut self, buffer: &Model, cx: &mut ModelContext) { let weak_buffer = buffer.downgrade(); self.buffers.insert(weak_buffer.clone()); @@ -646,7 +646,7 @@ impl Copilot { fn handle_buffer_event( &mut self, - buffer: Handle, + buffer: Model, event: &language2::Event, cx: &mut ModelContext, ) -> Result<()> { @@ -723,7 +723,7 @@ impl Copilot { pub fn completions( &mut self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> @@ -735,7 +735,7 @@ impl Copilot { pub fn completions_cycling( &mut self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> @@ -792,7 +792,7 @@ impl Copilot { fn request_completions( &mut self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> @@ -926,7 +926,7 @@ fn id_for_language(language: Option<&Arc>) -> String { } } -fn uri_for_buffer(buffer: &Handle, cx: &AppContext) -> lsp2::Url { +fn uri_for_buffer(buffer: &Model, cx: &AppContext) -> lsp2::Url { if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) { lsp2::Url::from_file_path(file.abs_path(cx)).unwrap() } else { diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 57ea79afbe..c4ba6a4724 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -707,19 +707,19 @@ impl AppContext { } impl Context for AppContext { - type EntityContext<'a, T> = ModelContext<'a, T>; + type ModelContext<'a, T> = ModelContext<'a, T>; type Result = T; /// Build an entity that is owned by the application. The given function will be invoked with /// a `ModelContext` and must return an object representing the entity. A `Handle` will be returned /// which can be used to access the entity in a context. - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Handle { + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Model { self.update(|cx| { let slot = cx.entities.reserve(); - let entity = build_entity(&mut ModelContext::mutable(cx, slot.downgrade())); + let entity = build_model(&mut ModelContext::mutable(cx, slot.downgrade())); cx.entities.insert(slot, entity) }) } @@ -728,8 +728,8 @@ impl Context for AppContext { /// entity along with a `ModelContext` for the entity. fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> R { self.update(|cx| { let mut entity = cx.entities.lease(handle); diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index d49f2ab934..71417f2a5e 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -1,5 +1,5 @@ use crate::{ - AnyWindowHandle, AppContext, Context, Executor, Handle, MainThread, ModelContext, Result, Task, + AnyWindowHandle, AppContext, Context, Executor, MainThread, Model, ModelContext, Result, Task, WindowContext, }; use anyhow::anyhow; @@ -14,13 +14,13 @@ pub struct AsyncAppContext { } impl Context for AsyncAppContext { - type EntityContext<'a, T> = ModelContext<'a, T>; + type ModelContext<'a, T> = ModelContext<'a, T>; type Result = Result; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Self::Result> + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Self::Result> where T: 'static + Send, { @@ -29,13 +29,13 @@ impl Context for AsyncAppContext { .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut lock = app.lock(); // Need this to compile - Ok(lock.entity(build_entity)) + Ok(lock.build_model(build_model)) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Self::Result { let app = self .app @@ -216,24 +216,24 @@ impl AsyncWindowContext { } impl Context for AsyncWindowContext { - type EntityContext<'a, T> = ModelContext<'a, T>; + type ModelContext<'a, T> = ModelContext<'a, T>; type Result = Result; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Result> + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Result> where T: 'static + Send, { self.app - .update_window(self.window, |cx| cx.entity(build_entity)) + .update_window(self.window, |cx| cx.build_model(build_model)) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Result { self.app .update_window(self.window, |cx| cx.update_entity(handle, update)) diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index f3ae67836d..68f0a8fa48 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/crates/gpui2/src/app/entity_map.rs @@ -53,11 +53,11 @@ impl EntityMap { /// Reserve a slot for an entity, which you can subsequently use with `insert`. pub fn reserve(&self) -> Slot { let id = self.ref_counts.write().counts.insert(1.into()); - Slot(Handle::new(id, Arc::downgrade(&self.ref_counts))) + Slot(Model::new(id, Arc::downgrade(&self.ref_counts))) } /// Insert an entity into a slot obtained by calling `reserve`. - pub fn insert(&mut self, slot: Slot, entity: T) -> Handle + pub fn insert(&mut self, slot: Slot, entity: T) -> Model where T: 'static + Send, { @@ -67,7 +67,7 @@ impl EntityMap { } /// Move an entity to the stack. - pub fn lease<'a, T>(&mut self, handle: &'a Handle) -> Lease<'a, T> { + pub fn lease<'a, T>(&mut self, handle: &'a Model) -> Lease<'a, T> { self.assert_valid_context(handle); let entity = Some( self.entities @@ -87,7 +87,7 @@ impl EntityMap { .insert(lease.handle.entity_id, lease.entity.take().unwrap()); } - pub fn read(&self, handle: &Handle) -> &T { + pub fn read(&self, handle: &Model) -> &T { self.assert_valid_context(handle); self.entities[handle.entity_id].downcast_ref().unwrap() } @@ -115,7 +115,7 @@ impl EntityMap { pub struct Lease<'a, T> { entity: Option, - pub handle: &'a Handle, + pub handle: &'a Model, entity_type: PhantomData, } @@ -143,7 +143,7 @@ impl<'a, T> Drop for Lease<'a, T> { } #[derive(Deref, DerefMut)] -pub struct Slot(Handle); +pub struct Slot(Model); pub struct AnyHandle { pub(crate) entity_id: EntityId, @@ -172,9 +172,9 @@ impl AnyHandle { } } - pub fn downcast(&self) -> Option> { + pub fn downcast(&self) -> Option> { if TypeId::of::() == self.entity_type { - Some(Handle { + Some(Model { any_handle: self.clone(), entity_type: PhantomData, }) @@ -223,8 +223,8 @@ impl Drop for AnyHandle { } } -impl From> for AnyHandle { - fn from(handle: Handle) -> Self { +impl From> for AnyHandle { + fn from(handle: Model) -> Self { handle.any_handle } } @@ -244,17 +244,17 @@ impl PartialEq for AnyHandle { impl Eq for AnyHandle {} #[derive(Deref, DerefMut)] -pub struct Handle { +pub struct Model { #[deref] #[deref_mut] any_handle: AnyHandle, entity_type: PhantomData, } -unsafe impl Send for Handle {} -unsafe impl Sync for Handle {} +unsafe impl Send for Model {} +unsafe impl Sync for Model {} -impl Handle { +impl Model { fn new(id: EntityId, entity_map: Weak>) -> Self where T: 'static, @@ -284,7 +284,7 @@ impl Handle { pub fn update( &self, cx: &mut C, - update: impl FnOnce(&mut T, &mut C::EntityContext<'_, T>) -> R, + update: impl FnOnce(&mut T, &mut C::ModelContext<'_, T>) -> R, ) -> C::Result where C: Context, @@ -293,7 +293,7 @@ impl Handle { } } -impl Clone for Handle { +impl Clone for Model { fn clone(&self) -> Self { Self { any_handle: self.any_handle.clone(), @@ -302,7 +302,7 @@ impl Clone for Handle { } } -impl std::fmt::Debug for Handle { +impl std::fmt::Debug for Model { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -313,21 +313,21 @@ impl std::fmt::Debug for Handle { } } -impl Hash for Handle { +impl Hash for Model { fn hash(&self, state: &mut H) { self.any_handle.hash(state); } } -impl PartialEq for Handle { +impl PartialEq for Model { fn eq(&self, other: &Self) -> bool { self.any_handle == other.any_handle } } -impl Eq for Handle {} +impl Eq for Model {} -impl PartialEq> for Handle { +impl PartialEq> for Model { fn eq(&self, other: &WeakHandle) -> bool { self.entity_id() == other.entity_id() } @@ -410,8 +410,8 @@ impl Clone for WeakHandle { } impl WeakHandle { - pub fn upgrade(&self) -> Option> { - Some(Handle { + pub fn upgrade(&self) -> Option> { + Some(Model { any_handle: self.any_handle.upgrade()?, entity_type: self.entity_type, }) @@ -427,7 +427,7 @@ impl WeakHandle { pub fn update( &self, cx: &mut C, - update: impl FnOnce(&mut T, &mut C::EntityContext<'_, T>) -> R, + update: impl FnOnce(&mut T, &mut C::ModelContext<'_, T>) -> R, ) -> Result where C: Context, @@ -455,8 +455,8 @@ impl PartialEq for WeakHandle { impl Eq for WeakHandle {} -impl PartialEq> for WeakHandle { - fn eq(&self, other: &Handle) -> bool { +impl PartialEq> for WeakHandle { + fn eq(&self, other: &Model) -> bool { self.entity_id() == other.entity_id() } } diff --git a/crates/gpui2/src/app/model_context.rs b/crates/gpui2/src/app/model_context.rs index effda37b73..b5f78fbc46 100644 --- a/crates/gpui2/src/app/model_context.rs +++ b/crates/gpui2/src/app/model_context.rs @@ -1,5 +1,5 @@ use crate::{ - AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, Handle, MainThread, + AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, MainThread, Model, Reference, Subscription, Task, WeakHandle, }; use derive_more::{Deref, DerefMut}; @@ -30,7 +30,7 @@ impl<'a, T: 'static> ModelContext<'a, T> { self.model_state.entity_id } - pub fn handle(&self) -> Handle { + pub fn handle(&self) -> Model { self.weak_handle() .upgrade() .expect("The entity must be alive if we have a model context") @@ -42,8 +42,8 @@ impl<'a, T: 'static> ModelContext<'a, T> { pub fn observe( &mut self, - handle: &Handle, - mut on_notify: impl FnMut(&mut T, Handle, &mut ModelContext<'_, T>) + Send + 'static, + handle: &Model, + mut on_notify: impl FnMut(&mut T, Model, &mut ModelContext<'_, T>) + Send + 'static, ) -> Subscription where T: 'static + Send, @@ -65,10 +65,8 @@ impl<'a, T: 'static> ModelContext<'a, T> { pub fn subscribe( &mut self, - handle: &Handle, - mut on_event: impl FnMut(&mut T, Handle, &E::Event, &mut ModelContext<'_, T>) - + Send - + 'static, + handle: &Model, + mut on_event: impl FnMut(&mut T, Model, &E::Event, &mut ModelContext<'_, T>) + Send + 'static, ) -> Subscription where T: 'static + Send, @@ -107,7 +105,7 @@ impl<'a, T: 'static> ModelContext<'a, T> { pub fn observe_release( &mut self, - handle: &Handle, + handle: &Model, mut on_release: impl FnMut(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + 'static, ) -> Subscription where @@ -224,23 +222,23 @@ where } impl<'a, T> Context for ModelContext<'a, T> { - type EntityContext<'b, U> = ModelContext<'b, U>; + type ModelContext<'b, U> = ModelContext<'b, U>; type Result = U; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, U>) -> U, - ) -> Handle + build_model: impl FnOnce(&mut Self::ModelContext<'_, U>) -> U, + ) -> Model where U: 'static + Send, { - self.app.entity(build_entity) + self.app.build_model(build_model) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, U>) -> R, + handle: &Model, + update: impl FnOnce(&mut U, &mut Self::ModelContext<'_, U>) -> R, ) -> R { self.app.update_entity(handle, update) } diff --git a/crates/gpui2/src/app/test_context.rs b/crates/gpui2/src/app/test_context.rs index 435349ca2a..dc5896fe06 100644 --- a/crates/gpui2/src/app/test_context.rs +++ b/crates/gpui2/src/app/test_context.rs @@ -1,5 +1,5 @@ use crate::{ - AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, Handle, MainThread, + AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, MainThread, Model, ModelContext, Result, Task, TestDispatcher, TestPlatform, WindowContext, }; use parking_lot::Mutex; @@ -12,24 +12,24 @@ pub struct TestAppContext { } impl Context for TestAppContext { - type EntityContext<'a, T> = ModelContext<'a, T>; + type ModelContext<'a, T> = ModelContext<'a, T>; type Result = T; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Self::Result> + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Self::Result> where T: 'static + Send, { let mut lock = self.app.lock(); - lock.entity(build_entity) + lock.build_model(build_model) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Self::Result { let mut lock = self.app.lock(); lock.update_entity(handle, update) diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index f6fa280c76..d85d1e0c74 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -70,20 +70,20 @@ use taffy::TaffyLayoutEngine; type AnyBox = Box; pub trait Context { - type EntityContext<'a, T>; + type ModelContext<'a, T>; type Result; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Self::Result> + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Self::Result> where T: 'static + Send; fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Self::Result; } @@ -92,7 +92,7 @@ pub trait VisualContext: Context { fn build_view( &mut self, - build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, + build_model: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, ) -> Self::Result> where @@ -130,37 +130,37 @@ impl DerefMut for MainThread { } impl Context for MainThread { - type EntityContext<'a, T> = MainThread>; + type ModelContext<'a, T> = MainThread>; type Result = C::Result; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Self::Result> + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Self::Result> where T: 'static + Send, { - self.0.entity(|cx| { + self.0.build_model(|cx| { let cx = unsafe { mem::transmute::< - &mut C::EntityContext<'_, T>, - &mut MainThread>, + &mut C::ModelContext<'_, T>, + &mut MainThread>, >(cx) }; - build_entity(cx) + build_model(cx) }) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Self::Result { self.0.update_entity(handle, |entity, cx| { let cx = unsafe { mem::transmute::< - &mut C::EntityContext<'_, T>, - &mut MainThread>, + &mut C::ModelContext<'_, T>, + &mut MainThread>, >(cx) }; update(entity, cx) @@ -173,7 +173,7 @@ impl VisualContext for MainThread { fn build_view( &mut self, - build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, + build_model: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, ) -> Self::Result> where @@ -188,7 +188,7 @@ impl VisualContext for MainThread { &mut MainThread>, >(cx) }; - build_entity(cx) + build_model(cx) }, render, ) diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index 4bb9c3d3a8..c988223fd0 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -1,7 +1,6 @@ use crate::{ AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId, - EntityId, Handle, LayoutId, Pixels, Size, ViewContext, VisualContext, WeakHandle, - WindowContext, + EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, WeakHandle, WindowContext, }; use anyhow::{Context, Result}; use parking_lot::Mutex; @@ -11,13 +10,13 @@ use std::{ }; pub struct View { - pub(crate) state: Handle, + pub(crate) state: Model, render: Arc) -> AnyElement + Send + 'static>>, } impl View { pub fn for_handle( - state: Handle, + state: Model, render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, ) -> View where diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index fd5aba6057..073ffa56bd 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -2,8 +2,8 @@ use crate::{ px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId, GlobalElementId, - GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, - Keystroke, LayoutId, MainThread, MainThreadOnly, ModelContext, Modifiers, MonochromeSprite, + GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, + LayoutId, MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, @@ -1240,25 +1240,25 @@ impl<'a, 'w> WindowContext<'a, 'w> { } impl Context for WindowContext<'_, '_> { - type EntityContext<'a, T> = ModelContext<'a, T>; + type ModelContext<'a, T> = ModelContext<'a, T>; type Result = T; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Handle + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Model where T: 'static + Send, { let slot = self.app.entities.reserve(); - let entity = build_entity(&mut ModelContext::mutable(&mut *self.app, slot.downgrade())); - self.entities.insert(slot, entity) + let model = build_model(&mut ModelContext::mutable(&mut *self.app, slot.downgrade())); + self.entities.insert(slot, model) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> R { let mut entity = self.entities.lease(handle); let result = update( @@ -1576,8 +1576,8 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { pub fn observe( &mut self, - handle: &Handle, - mut on_notify: impl FnMut(&mut V, Handle, &mut ViewContext<'_, '_, V>) + Send + 'static, + handle: &Model, + mut on_notify: impl FnMut(&mut V, Model, &mut ViewContext<'_, '_, V>) + Send + 'static, ) -> Subscription where E: 'static, @@ -1604,8 +1604,8 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { pub fn subscribe( &mut self, - handle: &Handle, - mut on_event: impl FnMut(&mut V, Handle, &E::Event, &mut ViewContext<'_, '_, V>) + handle: &Model, + mut on_event: impl FnMut(&mut V, Model, &E::Event, &mut ViewContext<'_, '_, V>) + Send + 'static, ) -> Subscription { @@ -1646,7 +1646,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { pub fn observe_release( &mut self, - handle: &Handle, + handle: &Model, mut on_release: impl FnMut(&mut V, &mut T, &mut ViewContext<'_, '_, V>) + Send + 'static, ) -> Subscription where @@ -1857,23 +1857,23 @@ where } impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> { - type EntityContext<'b, U> = ModelContext<'b, U>; + type ModelContext<'b, U> = ModelContext<'b, U>; type Result = U; - fn entity( + fn build_model( &mut self, - build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, - ) -> Handle + build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T, + ) -> Model where T: 'static + Send, { - self.window_cx.entity(build_entity) + self.window_cx.build_model(build_model) } fn update_entity( &mut self, - handle: &Handle, - update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, + handle: &Model, + update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> R { self.window_cx.update_entity(handle, update) } @@ -1884,14 +1884,14 @@ impl VisualContext for ViewContext<'_, '_, V> { fn build_view( &mut self, - build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V2>) -> V2, + build_view: impl FnOnce(&mut Self::ViewContext<'_, '_, V2>) -> V2, render: impl Fn(&mut V2, &mut ViewContext<'_, '_, V2>) -> E + Send + 'static, ) -> Self::Result> where E: crate::Component, V2: 'static + Send, { - self.window_cx.build_view(build_entity, render) + self.window_cx.build_view(build_view, render) } fn update_view( diff --git a/crates/language2/src/buffer_tests.rs b/crates/language2/src/buffer_tests.rs index fc60f31018..d2d886dd84 100644 --- a/crates/language2/src/buffer_tests.rs +++ b/crates/language2/src/buffer_tests.rs @@ -5,7 +5,7 @@ use crate::language_settings::{ use crate::Buffer; use clock::ReplicaId; use collections::BTreeMap; -use gpui2::{AppContext, Handle}; +use gpui2::{AppContext, Model}; use gpui2::{Context, TestAppContext}; use indoc::indoc; use proto::deserialize_operation; @@ -42,7 +42,7 @@ fn init_logger() { fn test_line_endings(cx: &mut gpui2::AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "one\r\ntwo\rthree") .with_language(Arc::new(rust_lang()), cx); assert_eq!(buffer.text(), "one\ntwo\nthree"); @@ -138,8 +138,8 @@ fn test_edit_events(cx: &mut gpui2::AppContext) { let buffer_1_events = Arc::new(Mutex::new(Vec::new())); let buffer_2_events = Arc::new(Mutex::new(Vec::new())); - let buffer1 = cx.entity(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcdef")); - let buffer2 = cx.entity(|cx| Buffer::new(1, cx.entity_id().as_u64(), "abcdef")); + let buffer1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcdef")); + let buffer2 = cx.build_model(|cx| Buffer::new(1, cx.entity_id().as_u64(), "abcdef")); let buffer1_ops = Arc::new(Mutex::new(Vec::new())); buffer1.update(cx, { let buffer1_ops = buffer1_ops.clone(); @@ -218,7 +218,7 @@ fn test_edit_events(cx: &mut gpui2::AppContext) { #[gpui2::test] async fn test_apply_diff(cx: &mut TestAppContext) { let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n"; - let buffer = cx.entity(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3))); let text = "a\nccc\ndddd\nffffff\n"; @@ -250,7 +250,7 @@ async fn test_normalize_whitespace(cx: &mut gpui2::TestAppContext) { ] .join("\n"); - let buffer = cx.entity(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); // Spawn a task to format the buffer's whitespace. // Pause so that the foratting task starts running. @@ -314,7 +314,7 @@ async fn test_normalize_whitespace(cx: &mut gpui2::TestAppContext) { #[gpui2::test] async fn test_reparse(cx: &mut gpui2::TestAppContext) { let text = "fn a() {}"; - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) }); @@ -442,7 +442,7 @@ async fn test_reparse(cx: &mut gpui2::TestAppContext) { #[gpui2::test] async fn test_resetting_language(cx: &mut gpui2::TestAppContext) { - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "{}").with_language(Arc::new(rust_lang()), cx); buffer.set_sync_parse_timeout(Duration::ZERO); @@ -492,7 +492,7 @@ async fn test_outline(cx: &mut gpui2::TestAppContext) { "# .unindent(); - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) }); let outline = buffer @@ -578,7 +578,7 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui2::TestAppContext) { "# .unindent(); - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) }); let outline = buffer @@ -616,7 +616,7 @@ async fn test_outline_with_extra_context(cx: &mut gpui2::TestAppContext) { "# .unindent(); - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(language), cx) }); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); @@ -660,7 +660,7 @@ async fn test_symbols_containing(cx: &mut gpui2::TestAppContext) { "# .unindent(); - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) }); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); @@ -881,7 +881,7 @@ fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: & #[gpui2::test] fn test_range_for_syntax_ancestor(cx: &mut AppContext) { - cx.entity(|cx| { + cx.build_model(|cx| { let text = "fn a() { b(|c| {}) }"; let buffer = Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); @@ -922,7 +922,7 @@ fn test_range_for_syntax_ancestor(cx: &mut AppContext) { fn test_autoindent_with_soft_tabs(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = "fn a() {}"; let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); @@ -965,7 +965,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) { settings.defaults.hard_tabs = Some(true); }); - cx.entity(|cx| { + cx.build_model(|cx| { let text = "fn a() {}"; let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); @@ -1006,7 +1006,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) { fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let entity_id = cx.entity_id(); let mut buffer = Buffer::new( 0, @@ -1080,7 +1080,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC buffer }); - cx.entity(|cx| { + cx.build_model(|cx| { eprintln!("second buffer: {:?}", cx.entity_id()); let mut buffer = Buffer::new( @@ -1147,7 +1147,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let mut buffer = Buffer::new( 0, cx.entity_id().as_u64(), @@ -1209,7 +1209,7 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let mut buffer = Buffer::new( 0, cx.entity_id().as_u64(), @@ -1266,7 +1266,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) { fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = "a\nb"; let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); @@ -1284,7 +1284,7 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) { fn test_autoindent_multi_line_insertion(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = " const a: usize = 1; fn b() { @@ -1326,7 +1326,7 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) { fn test_autoindent_block_mode(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = r#" fn a() { b(); @@ -1410,7 +1410,7 @@ fn test_autoindent_block_mode(cx: &mut AppContext) { fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = r#" fn a() { if b() { @@ -1490,7 +1490,7 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex fn test_autoindent_language_without_indents_query(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = " * one - a @@ -1559,7 +1559,7 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) { language_registry.add(html_language.clone()); language_registry.add(javascript_language.clone()); - cx.entity(|cx| { + cx.build_model(|cx| { let (text, ranges) = marked_text_ranges( &"
ˇ @@ -1610,7 +1610,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) { settings.defaults.tab_size = Some(2.try_into().unwrap()); }); - cx.entity(|cx| { + cx.build_model(|cx| { let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "").with_language(Arc::new(ruby_lang()), cx); @@ -1653,7 +1653,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) { fn test_language_scope_at_with_javascript(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let language = Language::new( LanguageConfig { name: "JavaScript".into(), @@ -1742,7 +1742,7 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) { fn test_language_scope_at_with_rust(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let language = Language::new( LanguageConfig { name: "Rust".into(), @@ -1810,7 +1810,7 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) { fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) { init_settings(cx, |_| {}); - cx.entity(|cx| { + cx.build_model(|cx| { let text = r#"
    <% people.each do |person| %> @@ -1858,7 +1858,7 @@ fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) { fn test_serialization(cx: &mut gpui2::AppContext) { let mut now = Instant::now(); - let buffer1 = cx.entity(|cx| { + let buffer1 = cx.build_model(|cx| { let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "abc"); buffer.edit([(3..3, "D")], None, cx); @@ -1881,7 +1881,7 @@ fn test_serialization(cx: &mut gpui2::AppContext) { let ops = cx .executor() .block(buffer1.read(cx).serialize_ops(None, cx)); - let buffer2 = cx.entity(|cx| { + let buffer2 = cx.build_model(|cx| { let mut buffer = Buffer::from_proto(1, state, None).unwrap(); buffer .apply_ops( @@ -1914,10 +1914,11 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { let mut replica_ids = Vec::new(); let mut buffers = Vec::new(); let network = Arc::new(Mutex::new(Network::new(rng.clone()))); - let base_buffer = cx.entity(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text.as_str())); + let base_buffer = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text.as_str())); for i in 0..rng.gen_range(min_peers..=max_peers) { - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { let state = base_buffer.read(cx).to_proto(); let ops = cx .executor() @@ -2034,7 +2035,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { new_replica_id, replica_id ); - new_buffer = Some(cx.entity(|cx| { + new_buffer = Some(cx.build_model(|cx| { let mut new_buffer = Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap(); new_buffer @@ -2396,7 +2397,7 @@ fn javascript_lang() -> Language { .unwrap() } -fn get_tree_sexp(buffer: &Handle, cx: &mut gpui2::TestAppContext) -> String { +fn get_tree_sexp(buffer: &Model, cx: &mut gpui2::TestAppContext) -> String { buffer.update(cx, |buffer, _| { let snapshot = buffer.snapshot(); let layers = snapshot.syntax.layers(buffer.as_text_snapshot()); @@ -2412,7 +2413,7 @@ fn assert_bracket_pairs( cx: &mut AppContext, ) { let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false); - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(0, cx.entity_id().as_u64(), expected_text.clone()) .with_language(Arc::new(language), cx) }); diff --git a/crates/prettier2/src/prettier2.rs b/crates/prettier2/src/prettier2.rs index 874ed10b91..52e5971a80 100644 --- a/crates/prettier2/src/prettier2.rs +++ b/crates/prettier2/src/prettier2.rs @@ -1,7 +1,7 @@ use anyhow::Context; use collections::{HashMap, HashSet}; use fs::Fs; -use gpui2::{AsyncAppContext, Handle}; +use gpui2::{AsyncAppContext, Model}; use language2::{language_settings::language_settings, Buffer, BundledFormatter, Diff}; use lsp2::{LanguageServer, LanguageServerId}; use node_runtime::NodeRuntime; @@ -183,7 +183,7 @@ impl Prettier { pub async fn format( &self, - buffer: &Handle, + buffer: &Model, buffer_path: Option, cx: &mut AsyncAppContext, ) -> anyhow::Result { diff --git a/crates/project2/src/lsp_command.rs b/crates/project2/src/lsp_command.rs index 3e5165b079..84a6c0517c 100644 --- a/crates/project2/src/lsp_command.rs +++ b/crates/project2/src/lsp_command.rs @@ -7,7 +7,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use client2::proto::{self, PeerId}; use futures::future; -use gpui2::{AppContext, AsyncAppContext, Handle}; +use gpui2::{AppContext, AsyncAppContext, Model}; use language2::{ language_settings::{language_settings, InlayHintKind}, point_from_lsp, point_to_lsp, @@ -53,8 +53,8 @@ pub(crate) trait LspCommand: 'static + Sized + Send { async fn response_from_lsp( self, message: ::Result, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, cx: AsyncAppContext, ) -> Result; @@ -63,8 +63,8 @@ pub(crate) trait LspCommand: 'static + Sized + Send { async fn from_proto( message: Self::ProtoRequest, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, cx: AsyncAppContext, ) -> Result; @@ -79,8 +79,8 @@ pub(crate) trait LspCommand: 'static + Sized + Send { async fn response_from_proto( self, message: ::Response, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, cx: AsyncAppContext, ) -> Result; @@ -180,8 +180,8 @@ impl LspCommand for PrepareRename { async fn response_from_lsp( self, message: Option, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, _: LanguageServerId, mut cx: AsyncAppContext, ) -> Result>> { @@ -215,8 +215,8 @@ impl LspCommand for PrepareRename { async fn from_proto( message: proto::PrepareRename, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -256,8 +256,8 @@ impl LspCommand for PrepareRename { async fn response_from_proto( self, message: proto::PrepareRenameResponse, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result>> { if message.can_rename { @@ -307,8 +307,8 @@ impl LspCommand for PerformRename { async fn response_from_lsp( self, message: Option, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, ) -> Result { @@ -343,8 +343,8 @@ impl LspCommand for PerformRename { async fn from_proto( message: proto::PerformRename, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -379,8 +379,8 @@ impl LspCommand for PerformRename { async fn response_from_proto( self, message: proto::PerformRenameResponse, - project: Handle, - _: Handle, + project: Model, + _: Model, mut cx: AsyncAppContext, ) -> Result { let message = message @@ -426,8 +426,8 @@ impl LspCommand for GetDefinition { async fn response_from_lsp( self, message: Option, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, cx: AsyncAppContext, ) -> Result> { @@ -447,8 +447,8 @@ impl LspCommand for GetDefinition { async fn from_proto( message: proto::GetDefinition, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -479,8 +479,8 @@ impl LspCommand for GetDefinition { async fn response_from_proto( self, message: proto::GetDefinitionResponse, - project: Handle, - _: Handle, + project: Model, + _: Model, cx: AsyncAppContext, ) -> Result> { location_links_from_proto(message.links, project, cx).await @@ -527,8 +527,8 @@ impl LspCommand for GetTypeDefinition { async fn response_from_lsp( self, message: Option, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, cx: AsyncAppContext, ) -> Result> { @@ -548,8 +548,8 @@ impl LspCommand for GetTypeDefinition { async fn from_proto( message: proto::GetTypeDefinition, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -580,8 +580,8 @@ impl LspCommand for GetTypeDefinition { async fn response_from_proto( self, message: proto::GetTypeDefinitionResponse, - project: Handle, - _: Handle, + project: Model, + _: Model, cx: AsyncAppContext, ) -> Result> { location_links_from_proto(message.links, project, cx).await @@ -593,8 +593,8 @@ impl LspCommand for GetTypeDefinition { } fn language_server_for_buffer( - project: &Handle, - buffer: &Handle, + project: &Model, + buffer: &Model, server_id: LanguageServerId, cx: &mut AsyncAppContext, ) -> Result<(Arc, Arc)> { @@ -609,7 +609,7 @@ fn language_server_for_buffer( async fn location_links_from_proto( proto_links: Vec, - project: Handle, + project: Model, mut cx: AsyncAppContext, ) -> Result> { let mut links = Vec::new(); @@ -671,8 +671,8 @@ async fn location_links_from_proto( async fn location_links_from_lsp( message: Option, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, ) -> Result> { @@ -814,8 +814,8 @@ impl LspCommand for GetReferences { async fn response_from_lsp( self, locations: Option>, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, ) -> Result> { @@ -868,8 +868,8 @@ impl LspCommand for GetReferences { async fn from_proto( message: proto::GetReferences, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -910,8 +910,8 @@ impl LspCommand for GetReferences { async fn response_from_proto( self, message: proto::GetReferencesResponse, - project: Handle, - _: Handle, + project: Model, + _: Model, mut cx: AsyncAppContext, ) -> Result> { let mut locations = Vec::new(); @@ -977,8 +977,8 @@ impl LspCommand for GetDocumentHighlights { async fn response_from_lsp( self, lsp_highlights: Option>, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, _: LanguageServerId, mut cx: AsyncAppContext, ) -> Result> { @@ -1016,8 +1016,8 @@ impl LspCommand for GetDocumentHighlights { async fn from_proto( message: proto::GetDocumentHighlights, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -1060,8 +1060,8 @@ impl LspCommand for GetDocumentHighlights { async fn response_from_proto( self, message: proto::GetDocumentHighlightsResponse, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result> { let mut highlights = Vec::new(); @@ -1123,8 +1123,8 @@ impl LspCommand for GetHover { async fn response_from_lsp( self, message: Option, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, _: LanguageServerId, mut cx: AsyncAppContext, ) -> Result { @@ -1206,8 +1206,8 @@ impl LspCommand for GetHover { async fn from_proto( message: Self::ProtoRequest, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -1272,8 +1272,8 @@ impl LspCommand for GetHover { async fn response_from_proto( self, message: proto::GetHoverResponse, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let contents: Vec<_> = message @@ -1341,8 +1341,8 @@ impl LspCommand for GetCompletions { async fn response_from_lsp( self, completions: Option, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, ) -> Result> { @@ -1484,8 +1484,8 @@ impl LspCommand for GetCompletions { async fn from_proto( message: proto::GetCompletions, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let version = deserialize_version(&message.version); @@ -1523,8 +1523,8 @@ impl LspCommand for GetCompletions { async fn response_from_proto( self, message: proto::GetCompletionsResponse, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result> { buffer @@ -1589,8 +1589,8 @@ impl LspCommand for GetCodeActions { async fn response_from_lsp( self, actions: Option, - _: Handle, - _: Handle, + _: Model, + _: Model, server_id: LanguageServerId, _: AsyncAppContext, ) -> Result> { @@ -1623,8 +1623,8 @@ impl LspCommand for GetCodeActions { async fn from_proto( message: proto::GetCodeActions, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let start = message @@ -1663,8 +1663,8 @@ impl LspCommand for GetCodeActions { async fn response_from_proto( self, message: proto::GetCodeActionsResponse, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result> { buffer @@ -1726,8 +1726,8 @@ impl LspCommand for OnTypeFormatting { async fn response_from_lsp( self, message: Option>, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, ) -> Result> { @@ -1763,8 +1763,8 @@ impl LspCommand for OnTypeFormatting { async fn from_proto( message: proto::OnTypeFormatting, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let position = message @@ -1805,8 +1805,8 @@ impl LspCommand for OnTypeFormatting { async fn response_from_proto( self, message: proto::OnTypeFormattingResponse, - _: Handle, - _: Handle, + _: Model, + _: Model, _: AsyncAppContext, ) -> Result> { let Some(transaction) = message.transaction else { @@ -1825,7 +1825,7 @@ impl LspCommand for OnTypeFormatting { impl InlayHints { pub async fn lsp_to_project_hint( lsp_hint: lsp2::InlayHint, - buffer_handle: &Handle, + buffer_handle: &Model, server_id: LanguageServerId, resolve_state: ResolveState, force_no_type_left_padding: bool, @@ -2230,8 +2230,8 @@ impl LspCommand for InlayHints { async fn response_from_lsp( self, message: Option>, - project: Handle, - buffer: Handle, + project: Model, + buffer: Model, server_id: LanguageServerId, mut cx: AsyncAppContext, ) -> anyhow::Result> { @@ -2286,8 +2286,8 @@ impl LspCommand for InlayHints { async fn from_proto( message: proto::InlayHints, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> Result { let start = message @@ -2326,8 +2326,8 @@ impl LspCommand for InlayHints { async fn response_from_proto( self, message: proto::InlayHintsResponse, - _: Handle, - buffer: Handle, + _: Model, + buffer: Model, mut cx: AsyncAppContext, ) -> anyhow::Result> { buffer diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index 9a3c2b34f2..e91c3b7263 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -26,7 +26,7 @@ use futures::{ }; use globset::{Glob, GlobSet, GlobSetBuilder}; use gpui2::{ - AnyHandle, AppContext, AsyncAppContext, Context, EventEmitter, Executor, Handle, ModelContext, + AnyHandle, AppContext, AsyncAppContext, Context, EventEmitter, Executor, Model, ModelContext, Task, WeakHandle, }; use itertools::Itertools; @@ -128,7 +128,7 @@ pub struct Project { next_entry_id: Arc, join_project_response_message_id: u32, next_diagnostic_group_id: usize, - user_store: Handle, + user_store: Model, fs: Arc, client_state: Option, collaborators: HashMap, @@ -140,17 +140,17 @@ pub struct Project { #[allow(clippy::type_complexity)] loading_buffers_by_path: HashMap< ProjectPath, - postage::watch::Receiver, Arc>>>, + postage::watch::Receiver, Arc>>>, >, #[allow(clippy::type_complexity)] loading_local_worktrees: - HashMap, Shared, Arc>>>>, + HashMap, Shared, Arc>>>>, opened_buffers: HashMap, local_buffer_ids_by_path: HashMap, local_buffer_ids_by_entry_id: HashMap, /// A mapping from a buffer ID to None means that we've started waiting for an ID but haven't finished loading it. /// Used for re-issuing buffer requests when peers temporarily disconnect - incomplete_remote_buffers: HashMap>>, + incomplete_remote_buffers: HashMap>>, buffer_snapshots: HashMap>>, // buffer_id -> server_id -> vec of snapshots buffers_being_formatted: HashSet, buffers_needing_diff: HashSet>, @@ -244,14 +244,14 @@ enum LocalProjectUpdate { } enum OpenBuffer { - Strong(Handle), + Strong(Model), Weak(WeakHandle), Operations(Vec), } #[derive(Clone)] enum WorktreeHandle { - Strong(Handle), + Strong(Model), Weak(WeakHandle), } @@ -344,7 +344,7 @@ pub struct DiagnosticSummary { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Location { - pub buffer: Handle, + pub buffer: Model, pub range: Range, } @@ -457,7 +457,7 @@ impl Hover { } #[derive(Default)] -pub struct ProjectTransaction(pub HashMap, language2::Transaction>); +pub struct ProjectTransaction(pub HashMap, language2::Transaction>); impl DiagnosticSummary { fn new<'a, T: 'a>(diagnostics: impl IntoIterator>) -> Self { @@ -527,7 +527,7 @@ pub enum FormatTrigger { } struct ProjectLspAdapterDelegate { - project: Handle, + project: Model, http_client: Arc, } @@ -543,7 +543,7 @@ impl FormatTrigger { #[derive(Clone, Debug, PartialEq)] enum SearchMatchCandidate { OpenBuffer { - buffer: Handle, + buffer: Model, // This might be an unnamed file without representation on filesystem path: Option>, }, @@ -621,12 +621,12 @@ impl Project { pub fn local( client: Arc, node: Arc, - user_store: Handle, + user_store: Model, languages: Arc, fs: Arc, cx: &mut AppContext, - ) -> Handle { - cx.entity(|cx: &mut ModelContext| { + ) -> Model { + cx.build_model(|cx: &mut ModelContext| { let (tx, rx) = mpsc::unbounded(); cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx)) .detach(); @@ -687,11 +687,11 @@ impl Project { pub async fn remote( remote_id: u64, client: Arc, - user_store: Handle, + user_store: Model, languages: Arc, fs: Arc, mut cx: AsyncAppContext, - ) -> Result> { + ) -> Result> { client.authenticate_and_connect(true, &cx).await?; let subscription = client.subscribe_to_entity(remote_id)?; @@ -700,7 +700,7 @@ impl Project { project_id: remote_id, }) .await?; - let this = cx.entity(|cx| { + let this = cx.build_model(|cx| { let replica_id = response.payload.replica_id as ReplicaId; let mut worktrees = Vec::new(); @@ -981,7 +981,7 @@ impl Project { cx.notify(); } - pub fn buffer_for_id(&self, remote_id: u64) -> Option> { + pub fn buffer_for_id(&self, remote_id: u64) -> Option> { self.opened_buffers .get(&remote_id) .and_then(|buffer| buffer.upgrade()) @@ -995,11 +995,11 @@ impl Project { self.client.clone() } - pub fn user_store(&self) -> Handle { + pub fn user_store(&self) -> Model { self.user_store.clone() } - pub fn opened_buffers(&self) -> Vec> { + pub fn opened_buffers(&self) -> Vec> { self.opened_buffers .values() .filter_map(|b| b.upgrade()) @@ -1061,7 +1061,7 @@ impl Project { } /// Collect all worktrees, including ones that don't appear in the project panel - pub fn worktrees<'a>(&'a self) -> impl 'a + DoubleEndedIterator> { + pub fn worktrees<'a>(&'a self) -> impl 'a + DoubleEndedIterator> { self.worktrees .iter() .filter_map(move |worktree| worktree.upgrade()) @@ -1071,7 +1071,7 @@ impl Project { pub fn visible_worktrees<'a>( &'a self, cx: &'a AppContext, - ) -> impl 'a + DoubleEndedIterator> { + ) -> impl 'a + DoubleEndedIterator> { self.worktrees.iter().filter_map(|worktree| { worktree.upgrade().and_then(|worktree| { if worktree.read(cx).is_visible() { @@ -1088,7 +1088,7 @@ impl Project { .map(|tree| tree.read(cx).root_name()) } - pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option> { + pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option> { self.worktrees() .find(|worktree| worktree.read(cx).id() == id) } @@ -1097,7 +1097,7 @@ impl Project { &self, entry_id: ProjectEntryId, cx: &AppContext, - ) -> Option> { + ) -> Option> { self.worktrees() .find(|worktree| worktree.read(cx).contains_entry(entry_id)) } @@ -1652,12 +1652,12 @@ impl Project { text: &str, language: Option>, cx: &mut ModelContext, - ) -> Result> { + ) -> Result> { if self.is_remote() { return Err(anyhow!("creating buffers as a guest is not supported yet")); } let id = post_inc(&mut self.next_buffer_id); - let buffer = cx.entity(|cx| { + let buffer = cx.build_model(|cx| { Buffer::new(self.replica_id(), id, text).with_language( language.unwrap_or_else(|| language2::PLAIN_TEXT.clone()), cx, @@ -1690,7 +1690,7 @@ impl Project { &mut self, abs_path: impl AsRef, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { if let Some((worktree, relative_path)) = self.find_local_worktree(abs_path.as_ref(), cx) { self.open_buffer((worktree.read(cx).id(), relative_path), cx) } else { @@ -1702,7 +1702,7 @@ impl Project { &mut self, path: impl Into, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let project_path = path.into(); let worktree = if let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) { worktree @@ -1757,9 +1757,9 @@ impl Project { fn open_local_buffer_internal( &mut self, path: &Arc, - worktree: &Handle, + worktree: &Model, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let buffer_id = post_inc(&mut self.next_buffer_id); let load_buffer = worktree.update(cx, |worktree, cx| { let worktree = worktree.as_local_mut().unwrap(); @@ -1775,9 +1775,9 @@ impl Project { fn open_remote_buffer_internal( &mut self, path: &Arc, - worktree: &Handle, + worktree: &Model, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let rpc = self.client.clone(); let project_id = self.remote_id().unwrap(); let remote_worktree_id = worktree.read(cx).id(); @@ -1805,7 +1805,7 @@ impl Project { language_server_id: LanguageServerId, language_server_name: LanguageServerName, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { cx.spawn(move |this, mut cx| async move { let abs_path = abs_path .to_file_path() @@ -1843,7 +1843,7 @@ impl Project { &mut self, id: u64, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { if let Some(buffer) = self.buffer_for_id(id) { Task::ready(Ok(buffer)) } else if self.is_local() { @@ -1866,7 +1866,7 @@ impl Project { pub fn save_buffers( &self, - buffers: HashSet>, + buffers: HashSet>, cx: &mut ModelContext, ) -> Task> { cx.spawn(move |this, mut cx| async move { @@ -1881,7 +1881,7 @@ impl Project { pub fn save_buffer( &self, - buffer: Handle, + buffer: Model, cx: &mut ModelContext, ) -> Task> { let Some(file) = File::from_dyn(buffer.read(cx).file()) else { @@ -1897,7 +1897,7 @@ impl Project { pub fn save_buffer_as( &mut self, - buffer: Handle, + buffer: Model, abs_path: PathBuf, cx: &mut ModelContext, ) -> Task> { @@ -1933,7 +1933,7 @@ impl Project { &mut self, path: &ProjectPath, cx: &mut ModelContext, - ) -> Option> { + ) -> Option> { let worktree = self.worktree_for_id(path.worktree_id, cx)?; self.opened_buffers.values().find_map(|buffer| { let buffer = buffer.upgrade()?; @@ -1948,7 +1948,7 @@ impl Project { fn register_buffer( &mut self, - buffer: &Handle, + buffer: &Model, cx: &mut ModelContext, ) -> Result<()> { self.request_buffer_diff_recalculation(buffer, cx); @@ -2030,7 +2030,7 @@ impl Project { fn register_buffer_with_language_servers( &mut self, - buffer_handle: &Handle, + buffer_handle: &Model, cx: &mut ModelContext, ) { let buffer = buffer_handle.read(cx); @@ -2114,7 +2114,7 @@ impl Project { fn unregister_buffer_from_language_servers( &mut self, - buffer: &Handle, + buffer: &Model, old_file: &File, cx: &mut ModelContext, ) { @@ -2149,7 +2149,7 @@ impl Project { fn register_buffer_with_copilot( &self, - buffer_handle: &Handle, + buffer_handle: &Model, cx: &mut ModelContext, ) { if let Some(copilot) = Copilot::global(cx) { @@ -2267,7 +2267,7 @@ impl Project { fn on_buffer_event( &mut self, - buffer: Handle, + buffer: Model, event: &BufferEvent, cx: &mut ModelContext, ) -> Option<()> { @@ -2480,7 +2480,7 @@ impl Project { fn request_buffer_diff_recalculation( &mut self, - buffer: &Handle, + buffer: &Model, cx: &mut ModelContext, ) { self.buffers_needing_diff.insert(buffer.downgrade()); @@ -2676,7 +2676,7 @@ impl Project { fn detect_language_for_buffer( &mut self, - buffer_handle: &Handle, + buffer_handle: &Model, cx: &mut ModelContext, ) -> Option<()> { // If the buffer has a language, set it and start the language server if we haven't already. @@ -2694,7 +2694,7 @@ impl Project { pub fn set_language_for_buffer( &mut self, - buffer: &Handle, + buffer: &Model, new_language: Arc, cx: &mut ModelContext, ) { @@ -2735,7 +2735,7 @@ impl Project { fn start_language_servers( &mut self, - worktree: &Handle, + worktree: &Model, worktree_path: Arc, language: Arc, cx: &mut ModelContext, @@ -3350,10 +3350,10 @@ impl Project { pub fn restart_language_servers_for_buffers( &mut self, - buffers: impl IntoIterator>, + buffers: impl IntoIterator>, cx: &mut ModelContext, ) -> Option<()> { - let language_server_lookup_info: HashSet<(Handle, Arc)> = buffers + let language_server_lookup_info: HashSet<(Model, Arc)> = buffers .into_iter() .filter_map(|buffer| { let buffer = buffer.read(cx); @@ -3377,7 +3377,7 @@ impl Project { // TODO This will break in the case where the adapter's root paths and worktrees are not equal fn restart_language_servers( &mut self, - worktree: Handle, + worktree: Model, language: Arc, cx: &mut ModelContext, ) { @@ -3949,7 +3949,7 @@ impl Project { fn update_buffer_diagnostics( &mut self, - buffer: &Handle, + buffer: &Model, server_id: LanguageServerId, version: Option, mut diagnostics: Vec>>, @@ -4022,7 +4022,7 @@ impl Project { pub fn reload_buffers( &self, - buffers: HashSet>, + buffers: HashSet>, push_to_history: bool, cx: &mut ModelContext, ) -> Task> { @@ -4088,7 +4088,7 @@ impl Project { pub fn format( &self, - buffers: HashSet>, + buffers: HashSet>, push_to_history: bool, trigger: FormatTrigger, cx: &mut ModelContext, @@ -4361,7 +4361,7 @@ impl Project { async fn format_via_lsp( this: &WeakHandle, - buffer: &Handle, + buffer: &Model, abs_path: &Path, language_server: &Arc, tab_size: NonZeroU32, @@ -4410,7 +4410,7 @@ impl Project { } async fn format_via_external_command( - buffer: &Handle, + buffer: &Model, buffer_abs_path: &Path, command: &str, arguments: &[String], @@ -4470,7 +4470,7 @@ impl Project { pub fn definition( &self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> { @@ -4485,7 +4485,7 @@ impl Project { pub fn type_definition( &self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> { @@ -4500,7 +4500,7 @@ impl Project { pub fn references( &self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> { @@ -4515,7 +4515,7 @@ impl Project { pub fn document_highlights( &self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> { @@ -4694,7 +4694,7 @@ impl Project { &mut self, symbol: &Symbol, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { if self.is_local() { let language_server_id = if let Some(id) = self.language_server_ids.get(&( symbol.source_worktree_id, @@ -4748,7 +4748,7 @@ impl Project { pub fn hover( &self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> { @@ -4763,7 +4763,7 @@ impl Project { pub fn completions( &self, - buffer: &Handle, + buffer: &Model, position: T, cx: &mut ModelContext, ) -> Task>> { @@ -4817,7 +4817,7 @@ impl Project { pub fn apply_additional_edits_for_completion( &self, - buffer_handle: Handle, + buffer_handle: Model, completion: Completion, push_to_history: bool, cx: &mut ModelContext, @@ -4928,7 +4928,7 @@ impl Project { pub fn code_actions( &self, - buffer_handle: &Handle, + buffer_handle: &Model, range: Range, cx: &mut ModelContext, ) -> Task>> { @@ -4944,7 +4944,7 @@ impl Project { pub fn apply_code_action( &self, - buffer_handle: Handle, + buffer_handle: Model, mut action: CodeAction, push_to_history: bool, cx: &mut ModelContext, @@ -5052,7 +5052,7 @@ impl Project { fn apply_on_type_formatting( &self, - buffer: Handle, + buffer: Model, position: Anchor, trigger: String, cx: &mut ModelContext, @@ -5113,8 +5113,8 @@ impl Project { } async fn deserialize_edits( - this: Handle, - buffer_to_edit: Handle, + this: Model, + buffer_to_edit: Model, edits: Vec, push_to_history: bool, _: Arc, @@ -5155,7 +5155,7 @@ impl Project { } async fn deserialize_workspace_edit( - this: Handle, + this: Model, edit: lsp2::WorkspaceEdit, push_to_history: bool, lsp_adapter: Arc, @@ -5310,7 +5310,7 @@ impl Project { pub fn prepare_rename( &self, - buffer: Handle, + buffer: Model, position: T, cx: &mut ModelContext, ) -> Task>>> { @@ -5325,7 +5325,7 @@ impl Project { pub fn perform_rename( &self, - buffer: Handle, + buffer: Model, position: T, new_name: String, push_to_history: bool, @@ -5346,7 +5346,7 @@ impl Project { pub fn on_type_format( &self, - buffer: Handle, + buffer: Model, position: T, trigger: String, push_to_history: bool, @@ -5375,7 +5375,7 @@ impl Project { pub fn inlay_hints( &self, - buffer_handle: Handle, + buffer_handle: Model, range: Range, cx: &mut ModelContext, ) -> Task>> { @@ -5436,7 +5436,7 @@ impl Project { pub fn resolve_inlay_hint( &self, hint: InlayHint, - buffer_handle: Handle, + buffer_handle: Model, server_id: LanguageServerId, cx: &mut ModelContext, ) -> Task> { @@ -5501,7 +5501,7 @@ impl Project { &self, query: SearchQuery, cx: &mut ModelContext, - ) -> Receiver<(Handle, Vec>)> { + ) -> Receiver<(Model, Vec>)> { if self.is_local() { self.search_local(query, cx) } else if let Some(project_id) = self.remote_id() { @@ -5545,7 +5545,7 @@ impl Project { &self, query: SearchQuery, cx: &mut ModelContext, - ) -> Receiver<(Handle, Vec>)> { + ) -> Receiver<(Model, Vec>)> { // Local search is split into several phases. // TL;DR is that we do 2 passes; initial pass to pick files which contain at least one match // and the second phase that finds positions of all the matches found in the candidate files. @@ -5638,7 +5638,7 @@ impl Project { .scoped(|scope| { #[derive(Clone)] struct FinishedStatus { - entry: Option<(Handle, Vec>)>, + entry: Option<(Model, Vec>)>, buffer_index: SearchMatchCandidateIndex, } @@ -5728,8 +5728,8 @@ impl Project { /// Pick paths that might potentially contain a match of a given search query. async fn background_search( - unnamed_buffers: Vec>, - opened_buffers: HashMap, (Handle, BufferSnapshot)>, + unnamed_buffers: Vec>, + opened_buffers: HashMap, (Model, BufferSnapshot)>, executor: Executor, fs: Arc, workers: usize, @@ -5829,7 +5829,7 @@ impl Project { fn request_lsp( &self, - buffer_handle: Handle, + buffer_handle: Model, server: LanguageServerToQuery, request: R, cx: &mut ModelContext, @@ -5893,7 +5893,7 @@ impl Project { fn send_lsp_proto_request( &self, - buffer: Handle, + buffer: Model, project_id: u64, request: R, cx: &mut ModelContext<'_, Project>, @@ -5922,7 +5922,7 @@ impl Project { ) -> ( futures::channel::oneshot::Receiver>, Receiver<( - Option<(Handle, BufferSnapshot)>, + Option<(Model, BufferSnapshot)>, SearchMatchCandidateIndex, )>, ) { @@ -5976,7 +5976,7 @@ impl Project { abs_path: impl AsRef, visible: bool, cx: &mut ModelContext, - ) -> Task, PathBuf)>> { + ) -> Task, PathBuf)>> { let abs_path = abs_path.as_ref(); if let Some((tree, relative_path)) = self.find_local_worktree(abs_path, cx) { Task::ready(Ok((tree, relative_path))) @@ -5991,7 +5991,7 @@ impl Project { &self, abs_path: &Path, cx: &AppContext, - ) -> Option<(Handle, PathBuf)> { + ) -> Option<(Model, PathBuf)> { for tree in &self.worktrees { if let Some(tree) = tree.upgrade() { if let Some(relative_path) = tree @@ -6018,7 +6018,7 @@ impl Project { abs_path: impl AsRef, visible: bool, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let fs = self.fs.clone(); let client = self.client.clone(); let next_entry_id = self.next_entry_id.clone(); @@ -6078,7 +6078,7 @@ impl Project { self.metadata_changed(cx); } - fn add_worktree(&mut self, worktree: &Handle, cx: &mut ModelContext) { + fn add_worktree(&mut self, worktree: &Model, cx: &mut ModelContext) { cx.observe(worktree, |_, _, cx| cx.notify()).detach(); if worktree.read(cx).is_local() { cx.subscribe(worktree, |this, worktree, event, cx| match event { @@ -6128,7 +6128,7 @@ impl Project { fn update_local_worktree_buffers( &mut self, - worktree_handle: &Handle, + worktree_handle: &Model, changes: &[(Arc, ProjectEntryId, PathChange)], cx: &mut ModelContext, ) { @@ -6242,7 +6242,7 @@ impl Project { fn update_local_worktree_language_servers( &mut self, - worktree_handle: &Handle, + worktree_handle: &Model, changes: &[(Arc, ProjectEntryId, PathChange)], cx: &mut ModelContext, ) { @@ -6304,7 +6304,7 @@ impl Project { fn update_local_worktree_buffers_git_repos( &mut self, - worktree_handle: Handle, + worktree_handle: Model, changed_repos: &UpdatedGitRepositoriesSet, cx: &mut ModelContext, ) { @@ -6407,7 +6407,7 @@ impl Project { fn update_local_worktree_settings( &mut self, - worktree: &Handle, + worktree: &Model, changes: &UpdatedEntriesSet, cx: &mut ModelContext, ) { @@ -6473,7 +6473,7 @@ impl Project { fn update_prettier_settings( &self, - worktree: &Handle, + worktree: &Model, changes: &[(Arc, ProjectEntryId, PathChange)], cx: &mut ModelContext<'_, Project>, ) { @@ -6636,7 +6636,7 @@ impl Project { // RPC message handlers async fn handle_unshare_project( - this: Handle, + this: Model, _: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6652,7 +6652,7 @@ impl Project { } async fn handle_add_collaborator( - this: Handle, + this: Model, mut envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6676,7 +6676,7 @@ impl Project { } async fn handle_update_project_collaborator( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6726,7 +6726,7 @@ impl Project { } async fn handle_remove_collaborator( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6755,7 +6755,7 @@ impl Project { } async fn handle_update_project( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6770,7 +6770,7 @@ impl Project { } async fn handle_update_worktree( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6788,7 +6788,7 @@ impl Project { } async fn handle_update_worktree_settings( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6812,7 +6812,7 @@ impl Project { } async fn handle_create_project_entry( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6837,7 +6837,7 @@ impl Project { } async fn handle_rename_project_entry( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6865,7 +6865,7 @@ impl Project { } async fn handle_copy_project_entry( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6893,7 +6893,7 @@ impl Project { } async fn handle_delete_project_entry( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6923,7 +6923,7 @@ impl Project { } async fn handle_expand_project_entry( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6946,7 +6946,7 @@ impl Project { } async fn handle_update_diagnostic_summary( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -6976,7 +6976,7 @@ impl Project { } async fn handle_start_language_server( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7001,7 +7001,7 @@ impl Project { } async fn handle_update_language_server( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7058,7 +7058,7 @@ impl Project { } async fn handle_update_buffer( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7094,7 +7094,7 @@ impl Project { } async fn handle_create_buffer_for_peer( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7117,7 +7117,7 @@ impl Project { } let buffer_id = state.id; - let buffer = cx.entity(|_| { + let buffer = cx.build_model(|_| { Buffer::from_proto(this.replica_id(), state, buffer_file).unwrap() }); this.incomplete_remote_buffers @@ -7154,7 +7154,7 @@ impl Project { } async fn handle_update_diff_base( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7180,7 +7180,7 @@ impl Project { } async fn handle_update_buffer_file( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7215,7 +7215,7 @@ impl Project { } async fn handle_save_buffer( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7251,7 +7251,7 @@ impl Project { } async fn handle_reload_buffers( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7280,7 +7280,7 @@ impl Project { } async fn handle_synchronize_buffers( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7373,7 +7373,7 @@ impl Project { } async fn handle_format_buffers( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7403,7 +7403,7 @@ impl Project { } async fn handle_apply_additional_edits_for_completion( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7440,7 +7440,7 @@ impl Project { } async fn handle_apply_code_action( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7471,7 +7471,7 @@ impl Project { } async fn handle_on_type_formatting( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7503,7 +7503,7 @@ impl Project { } async fn handle_inlay_hints( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7553,7 +7553,7 @@ impl Project { } async fn handle_resolve_inlay_hint( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7587,7 +7587,7 @@ impl Project { } async fn handle_refresh_inlay_hints( - this: Handle, + this: Model, _: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7599,7 +7599,7 @@ impl Project { } async fn handle_lsp_command( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7641,7 +7641,7 @@ impl Project { } async fn handle_get_project_symbols( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7658,7 +7658,7 @@ impl Project { } async fn handle_search_project( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7689,7 +7689,7 @@ impl Project { } async fn handle_open_buffer_for_symbol( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7730,7 +7730,7 @@ impl Project { } async fn handle_open_buffer_by_id( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7749,7 +7749,7 @@ impl Project { } async fn handle_open_buffer_by_path( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -7834,7 +7834,7 @@ impl Project { fn create_buffer_for_peer( &mut self, - buffer: &Handle, + buffer: &Model, peer_id: proto::PeerId, cx: &mut AppContext, ) -> u64 { @@ -7851,7 +7851,7 @@ impl Project { &mut self, id: u64, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let mut opened_buffer_rx = self.opened_buffer.1.clone(); cx.spawn(move |this, mut cx| async move { @@ -8108,7 +8108,7 @@ impl Project { } async fn handle_buffer_saved( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -8141,7 +8141,7 @@ impl Project { } async fn handle_buffer_reloaded( - this: Handle, + this: Model, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, @@ -8180,7 +8180,7 @@ impl Project { #[allow(clippy::type_complexity)] fn edits_from_lsp( &mut self, - buffer: &Handle, + buffer: &Model, lsp_edits: impl 'static + Send + IntoIterator, server_id: LanguageServerId, version: Option, @@ -8284,7 +8284,7 @@ impl Project { fn buffer_snapshot_for_lsp_version( &mut self, - buffer: &Handle, + buffer: &Model, server_id: LanguageServerId, version: Option, cx: &AppContext, @@ -8402,7 +8402,7 @@ impl Project { fn prettier_instance_for_buffer( &mut self, - buffer: &Handle, + buffer: &Model, cx: &mut ModelContext, ) -> Task, Arc>>>>> { let buffer = buffer.read(cx); @@ -8638,7 +8638,7 @@ impl Project { } fn subscribe_for_copilot_events( - copilot: &Handle, + copilot: &Model, cx: &mut ModelContext<'_, Project>, ) -> gpui2::Subscription { cx.subscribe( @@ -8691,7 +8691,7 @@ fn glob_literal_prefix<'a>(glob: &'a str) -> &'a str { } impl WorktreeHandle { - pub fn upgrade(&self) -> Option> { + pub fn upgrade(&self) -> Option> { match self { WorktreeHandle::Strong(handle) => Some(handle.clone()), WorktreeHandle::Weak(handle) => handle.upgrade(), @@ -8707,7 +8707,7 @@ impl WorktreeHandle { } impl OpenBuffer { - pub fn upgrade(&self) -> Option> { + pub fn upgrade(&self) -> Option> { match self { OpenBuffer::Strong(handle) => Some(handle.clone()), OpenBuffer::Weak(handle) => handle.upgrade(), @@ -8871,8 +8871,8 @@ impl Item for Buffer { } async fn wait_for_loading_buffer( - mut receiver: postage::watch::Receiver, Arc>>>, -) -> Result, Arc> { + mut receiver: postage::watch::Receiver, Arc>>>, +) -> Result, Arc> { loop { if let Some(result) = receiver.borrow().as_ref() { match result { diff --git a/crates/project2/src/terminals.rs b/crates/project2/src/terminals.rs index f958142437..239cb99d86 100644 --- a/crates/project2/src/terminals.rs +++ b/crates/project2/src/terminals.rs @@ -1,5 +1,5 @@ use crate::Project; -use gpui2::{AnyWindowHandle, Context, Handle, ModelContext, WeakHandle}; +use gpui2::{AnyWindowHandle, Context, Model, ModelContext, WeakHandle}; use settings2::Settings; use std::path::{Path, PathBuf}; use terminal2::{ @@ -20,7 +20,7 @@ impl Project { working_directory: Option, window: AnyWindowHandle, cx: &mut ModelContext, - ) -> anyhow::Result> { + ) -> anyhow::Result> { if self.is_remote() { return Err(anyhow::anyhow!( "creating terminals as a guest is not supported yet" @@ -40,7 +40,7 @@ impl Project { |_, _| todo!("color_for_index"), ) .map(|builder| { - let terminal_handle = cx.entity(|cx| builder.subscribe(cx)); + let terminal_handle = cx.build_model(|cx| builder.subscribe(cx)); self.terminals .local_handles @@ -108,7 +108,7 @@ impl Project { fn activate_python_virtual_environment( &mut self, activate_script: Option, - terminal_handle: &Handle, + terminal_handle: &Model, cx: &mut ModelContext, ) { if let Some(activate_script) = activate_script { diff --git a/crates/project2/src/worktree.rs b/crates/project2/src/worktree.rs index c1b762640b..00ab10d8e8 100644 --- a/crates/project2/src/worktree.rs +++ b/crates/project2/src/worktree.rs @@ -22,7 +22,7 @@ use futures::{ use fuzzy2::CharBag; use git::{DOT_GIT, GITIGNORE}; use gpui2::{ - AppContext, AsyncAppContext, Context, EventEmitter, Executor, Handle, ModelContext, Task, + AppContext, AsyncAppContext, Context, EventEmitter, Executor, Model, ModelContext, Task, }; use language2::{ proto::{ @@ -292,7 +292,7 @@ impl Worktree { fs: Arc, next_entry_id: Arc, cx: &mut AsyncAppContext, - ) -> Result> { + ) -> Result> { // After determining whether the root entry is a file or a directory, populate the // snapshot's "root name", which will be used for the purpose of fuzzy matching. let abs_path = path.into(); @@ -301,7 +301,7 @@ impl Worktree { .await .context("failed to stat worktree path")?; - cx.entity(move |cx: &mut ModelContext| { + cx.build_model(move |cx: &mut ModelContext| { let root_name = abs_path .file_name() .map_or(String::new(), |f| f.to_string_lossy().to_string()); @@ -406,8 +406,8 @@ impl Worktree { worktree: proto::WorktreeMetadata, client: Arc, cx: &mut AppContext, - ) -> Handle { - cx.entity(|cx: &mut ModelContext| { + ) -> Model { + cx.build_model(|cx: &mut ModelContext| { let snapshot = Snapshot { id: WorktreeId(worktree.id as usize), abs_path: Arc::from(PathBuf::from(worktree.abs_path)), @@ -593,7 +593,7 @@ impl LocalWorktree { id: u64, path: &Path, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let path = Arc::from(path); cx.spawn(move |this, mut cx| async move { let (file, contents, diff_base) = this @@ -603,7 +603,7 @@ impl LocalWorktree { .executor() .spawn(async move { text::Buffer::new(0, id, contents) }) .await; - cx.entity(|_| Buffer::build(text_buffer, diff_base, Some(Arc::new(file)))) + cx.build_model(|_| Buffer::build(text_buffer, diff_base, Some(Arc::new(file)))) }) } @@ -920,7 +920,7 @@ impl LocalWorktree { pub fn save_buffer( &self, - buffer_handle: Handle, + buffer_handle: Model, path: Arc, has_changed_file: bool, cx: &mut ModelContext, @@ -1331,7 +1331,7 @@ impl RemoteWorktree { pub fn save_buffer( &self, - buffer_handle: Handle, + buffer_handle: Model, cx: &mut ModelContext, ) -> Task> { let buffer = buffer_handle.read(cx); @@ -2577,7 +2577,7 @@ impl fmt::Debug for Snapshot { #[derive(Clone, PartialEq)] pub struct File { - pub worktree: Handle, + pub worktree: Model, pub path: Arc, pub mtime: SystemTime, pub(crate) entry_id: ProjectEntryId, @@ -2701,7 +2701,7 @@ impl language2::LocalFile for File { } impl File { - pub fn for_entry(entry: Entry, worktree: Handle) -> Arc { + pub fn for_entry(entry: Entry, worktree: Model) -> Arc { Arc::new(Self { worktree, path: entry.path.clone(), @@ -2714,7 +2714,7 @@ impl File { pub fn from_proto( proto: rpc2::proto::File, - worktree: Handle, + worktree: Model, cx: &AppContext, ) -> Result { let worktree_id = worktree diff --git a/crates/storybook2/src/stories/kitchen_sink.rs b/crates/storybook2/src/stories/kitchen_sink.rs index 3a4b127b55..406eb33853 100644 --- a/crates/storybook2/src/stories/kitchen_sink.rs +++ b/crates/storybook2/src/stories/kitchen_sink.rs @@ -14,7 +14,7 @@ impl KitchenSinkStory { pub fn view(cx: &mut AppContext) -> View { { - let state = cx.entity(|cx| Self::new()); + let state = cx.build_model(|cx| Self::new()); let render = Self::render; View::for_handle(state, render) } diff --git a/crates/ui2/src/components/buffer_search.rs b/crates/ui2/src/components/buffer_search.rs index fa7f752ffe..85a74930ce 100644 --- a/crates/ui2/src/components/buffer_search.rs +++ b/crates/ui2/src/components/buffer_search.rs @@ -23,7 +23,7 @@ impl BufferSearch { pub fn view(cx: &mut AppContext) -> View { { - let state = cx.entity(|cx| Self::new()); + let state = cx.build_model(|cx| Self::new()); let render = Self::render; View::for_handle(state, render) } diff --git a/crates/ui2/src/components/editor_pane.rs b/crates/ui2/src/components/editor_pane.rs index ec73bb805f..33f9051dc9 100644 --- a/crates/ui2/src/components/editor_pane.rs +++ b/crates/ui2/src/components/editor_pane.rs @@ -44,7 +44,7 @@ impl EditorPane { pub fn view(cx: &mut AppContext) -> View { { - let state = cx.entity(|cx| hello_world_rust_editor_with_status_example(cx)); + let state = cx.build_model(|cx| hello_world_rust_editor_with_status_example(cx)); let render = Self::render; View::for_handle(state, render) } diff --git a/crates/ui2/src/components/title_bar.rs b/crates/ui2/src/components/title_bar.rs index 11dd1b99f6..29622d1395 100644 --- a/crates/ui2/src/components/title_bar.rs +++ b/crates/ui2/src/components/title_bar.rs @@ -82,7 +82,7 @@ impl TitleBar { pub fn view(cx: &mut AppContext, livestream: Option) -> View { { - let state = cx.entity(|cx| Self::new(cx).set_livestream(livestream)); + let state = cx.build_model(|cx| Self::new(cx).set_livestream(livestream)); let render = Self::render; View::for_handle(state, render) } @@ -198,7 +198,7 @@ mod stories { impl TitleBarStory { pub fn view(cx: &mut AppContext) -> View { { - let state = cx.entity(|cx| Self { + let state = cx.build_model(|cx| Self { title_bar: TitleBar::view(cx, None), }); let render = Self::render; diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index af2485a7a9..f2314fb377 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -3,12 +3,13 @@ use std::sync::Arc; use chrono::DateTime; use gpui2::{px, relative, rems, AppContext, Context, Size, View}; -use crate::{static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage, - ChatPanel, CollabPanel, EditorPane, FakeSettings, Label, LanguageSelector, Pane, PaneGroup, - Panel, PanelAllowedSides, PanelSide, ProjectPanel, SettingValue, SplitDirection, StatusBar, - Terminal, TitleBar, Toast, ToastOrigin, -}; use crate::{prelude::*, NotificationsPanel}; +use crate::{ + static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, + CollabPanel, EditorPane, FakeSettings, Label, LanguageSelector, Pane, PaneGroup, Panel, + PanelAllowedSides, PanelSide, ProjectPanel, SettingValue, SplitDirection, StatusBar, Terminal, + TitleBar, Toast, ToastOrigin, +}; #[derive(Clone)] pub struct Gpui2UiDebug { @@ -171,7 +172,7 @@ impl Workspace { pub fn view(cx: &mut AppContext) -> View { { - let state = cx.entity(|cx| Self::new(cx)); + let state = cx.build_model(|cx| Self::new(cx)); let render = Self::render; View::for_handle(state, render) } diff --git a/crates/workspace2/src/item.rs b/crates/workspace2/src/item.rs new file mode 100644 index 0000000000..90d08d6c4a --- /dev/null +++ b/crates/workspace2/src/item.rs @@ -0,0 +1,1096 @@ +// use crate::{ +// pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders, +// ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId, +// }; +// use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings}; +use anyhow::Result; +use client2::{ + proto::{self, PeerId, ViewId}, + Client, +}; +use settings2::Settings; +use theme2::Theme; +// use client2::{ +// proto::{self, PeerId}, +// Client, +// }; +// use gpui2::geometry::vector::Vector2F; +// use gpui2::AnyWindowHandle; +// use gpui2::{ +// fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, Handle, Task, View, +// ViewContext, View, WeakViewHandle, WindowContext, +// }; +// use project2::{Project, ProjectEntryId, ProjectPath}; +// use schemars::JsonSchema; +// use serde_derive::{Deserialize, Serialize}; +// use settings2::Setting; +// use smallvec::SmallVec; +// use std::{ +// any::{Any, TypeId}, +// borrow::Cow, +// cell::RefCell, +// fmt, +// ops::Range, +// path::PathBuf, +// rc::Rc, +// sync::{ +// atomic::{AtomicBool, Ordering}, +// Arc, +// }, +// time::Duration, +// }; +// use theme2::Theme; + +// #[derive(Deserialize)] +// pub struct ItemSettings { +// pub git_status: bool, +// pub close_position: ClosePosition, +// } + +// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +// #[serde(rename_all = "lowercase")] +// pub enum ClosePosition { +// Left, +// #[default] +// Right, +// } + +// impl ClosePosition { +// pub fn right(&self) -> bool { +// match self { +// ClosePosition::Left => false, +// ClosePosition::Right => true, +// } +// } +// } + +// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +// pub struct ItemSettingsContent { +// git_status: Option, +// close_position: Option, +// } + +// impl Setting for ItemSettings { +// const KEY: Option<&'static str> = Some("tabs"); + +// type FileContent = ItemSettingsContent; + +// fn load( +// default_value: &Self::FileContent, +// user_values: &[&Self::FileContent], +// _: &gpui2::AppContext, +// ) -> anyhow::Result { +// Self::load_via_json_merge(default_value, user_values) +// } +// } + +#[derive(Eq, PartialEq, Hash, Debug)] +pub enum ItemEvent { + CloseItem, + UpdateTab, + UpdateBreadcrumbs, + Edit, +} + +// TODO: Combine this with existing HighlightedText struct? +pub struct BreadcrumbText { + pub text: String, + pub highlights: Option, HighlightStyle)>>, +} + +pub trait Item: EventEmitter + Sized { + // fn deactivated(&mut self, _: &mut ViewContext) {} + // fn workspace_deactivated(&mut self, _: &mut ViewContext) {} + // fn navigate(&mut self, _: Box, _: &mut ViewContext) -> bool { + // false + // } + fn tab_tooltip_text(&self, _: &AppContext) -> Option { + None + } + fn tab_description(&self, _: usize, _: &AppContext) -> Option { + None + } + fn tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement; + + fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)) { + } // (model id, Item) + fn is_singleton(&self, _cx: &AppContext) -> bool { + false + } + // fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext) {} + fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext) -> Option + where + Self: Sized, + { + None + } + // fn is_dirty(&self, _: &AppContext) -> bool { + // false + // } + // fn has_conflict(&self, _: &AppContext) -> bool { + // false + // } + // fn can_save(&self, _cx: &AppContext) -> bool { + // false + // } + // fn save( + // &mut self, + // _project: Handle, + // _cx: &mut ViewContext, + // ) -> Task> { + // unimplemented!("save() must be implemented if can_save() returns true") + // } + // fn save_as( + // &mut self, + // _project: Handle, + // _abs_path: PathBuf, + // _cx: &mut ViewContext, + // ) -> Task> { + // unimplemented!("save_as() must be implemented if can_save() returns true") + // } + // fn reload( + // &mut self, + // _project: Handle, + // _cx: &mut ViewContext, + // ) -> Task> { + // unimplemented!("reload() must be implemented if can_save() returns true") + // } + fn to_item_events(_event: &Self::Event) -> SmallVec<[ItemEvent; 2]> { + SmallVec::new() + } + // fn should_close_item_on_event(_: &Self::Event) -> bool { + // false + // } + // fn should_update_tab_on_event(_: &Self::Event) -> bool { + // false + // } + + // fn act_as_type<'a>( + // &'a self, + // type_id: TypeId, + // self_handle: &'a View, + // _: &'a AppContext, + // ) -> Option<&AnyViewHandle> { + // if TypeId::of::() == type_id { + // Some(self_handle) + // } else { + // None + // } + // } + + // fn as_searchable(&self, _: &View) -> Option> { + // None + // } + + // fn breadcrumb_location(&self) -> ToolbarItemLocation { + // ToolbarItemLocation::Hidden + // } + + // fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option> { + // None + // } + + // fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext) {} + + // fn serialized_item_kind() -> Option<&'static str> { + // None + // } + + // fn deserialize( + // _project: Handle, + // _workspace: WeakViewHandle, + // _workspace_id: WorkspaceId, + // _item_id: ItemId, + // _cx: &mut ViewContext, + // ) -> Task>> { + // unimplemented!( + // "deserialize() must be implemented if serialized_item_kind() returns Some(_)" + // ) + // } + // fn show_toolbar(&self) -> bool { + // true + // } + // fn pixel_position_of_cursor(&self, _: &AppContext) -> Option { + // None + // } +} + +use std::{ + any::Any, + cell::RefCell, + ops::Range, + path::PathBuf, + rc::Rc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; + +use gpui2::{ + AnyElement, AnyWindowHandle, AppContext, EventEmitter, Handle, HighlightStyle, Pixels, Point, + SharedString, Task, View, ViewContext, VisualContext, WindowContext, +}; +use project2::{Project, ProjectEntryId, ProjectPath}; +use smallvec::SmallVec; + +use crate::{ + pane::{self, Pane}, + searchable::SearchableItemHandle, + workspace_settings::{AutosaveSetting, WorkspaceSettings}, + DelayedDebouncedEditAction, FollowableItemBuilders, ToolbarItemLocation, Workspace, + WorkspaceId, +}; + +pub trait ItemHandle: 'static + Send { + fn subscribe_to_item_events( + &self, + cx: &mut WindowContext, + handler: Box, + ) -> gpui2::Subscription; + fn tab_tooltip_text(&self, cx: &AppContext) -> Option; + fn tab_description(&self, detail: usize, cx: &AppContext) -> Option; + fn tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement; + fn dragged_tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement; + fn project_path(&self, cx: &AppContext) -> Option; + fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>; + fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]>; + fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)); + fn is_singleton(&self, cx: &AppContext) -> bool; + fn boxed_clone(&self) -> Box; + fn clone_on_split( + &self, + workspace_id: WorkspaceId, + cx: &mut WindowContext, + ) -> Option>; + fn added_to_pane( + &self, + workspace: &mut Workspace, + pane: View, + cx: &mut ViewContext, + ); + fn deactivated(&self, cx: &mut WindowContext); + fn workspace_deactivated(&self, cx: &mut WindowContext); + fn navigate(&self, data: Box, cx: &mut WindowContext) -> bool; + fn id(&self) -> usize; + fn window(&self) -> AnyWindowHandle; + // fn as_any(&self) -> &AnyView; todo!() + fn is_dirty(&self, cx: &AppContext) -> bool; + fn has_conflict(&self, cx: &AppContext) -> bool; + fn can_save(&self, cx: &AppContext) -> bool; + fn save(&self, project: Handle, cx: &mut WindowContext) -> Task>; + fn save_as( + &self, + project: Handle, + abs_path: PathBuf, + cx: &mut WindowContext, + ) -> Task>; + fn reload(&self, project: Handle, cx: &mut WindowContext) -> Task>; + // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>; todo!() + fn to_followable_item_handle(&self, cx: &AppContext) -> Option>; + fn on_release( + &self, + cx: &mut AppContext, + callback: Box, + ) -> gpui2::Subscription; + fn to_searchable_item_handle(&self, cx: &AppContext) -> Option>; + fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation; + fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option>; + fn serialized_item_kind(&self) -> Option<&'static str>; + fn show_toolbar(&self, cx: &AppContext) -> bool; + fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option>; +} + +pub trait WeakItemHandle { + fn id(&self) -> usize; + fn window(&self) -> AnyWindowHandle; + fn upgrade(&self, cx: &AppContext) -> Option>; +} + +// todo!() +// impl dyn ItemHandle { +// pub fn downcast(&self) -> Option> { +// self.as_any().clone().downcast() +// } + +// pub fn act_as(&self, cx: &AppContext) -> Option> { +// self.act_as_type(TypeId::of::(), cx) +// .and_then(|t| t.clone().downcast()) +// } +// } + +impl ItemHandle for View { + fn subscribe_to_item_events( + &self, + cx: &mut WindowContext, + handler: Box, + ) -> gpui2::Subscription { + cx.subscribe(self, move |_, event, cx| { + for item_event in T::to_item_events(event) { + handler(item_event, cx) + } + }) + } + + fn tab_tooltip_text(&self, cx: &AppContext) -> Option { + self.read(cx).tab_tooltip_text(cx) + } + + fn tab_description(&self, detail: usize, cx: &AppContext) -> Option { + self.read(cx).tab_description(detail, cx) + } + + fn tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement { + self.read(cx).tab_content(detail, cx) + } + + fn dragged_tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement { + self.read(cx).tab_content(detail, cx) + } + + fn project_path(&self, cx: &AppContext) -> Option { + let this = self.read(cx); + let mut result = None; + if this.is_singleton(cx) { + this.for_each_project_item(cx, &mut |_, item| { + result = item.project_path(cx); + }); + } + result + } + + fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]> { + let mut result = SmallVec::new(); + self.read(cx).for_each_project_item(cx, &mut |_, item| { + if let Some(id) = item.entry_id(cx) { + result.push(id); + } + }); + result + } + + fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]> { + let mut result = SmallVec::new(); + self.read(cx).for_each_project_item(cx, &mut |id, _| { + result.push(id); + }); + result + } + + fn for_each_project_item( + &self, + cx: &AppContext, + f: &mut dyn FnMut(usize, &dyn project2::Item), + ) { + self.read(cx).for_each_project_item(cx, f) + } + + fn is_singleton(&self, cx: &AppContext) -> bool { + self.read(cx).is_singleton(cx) + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn clone_on_split( + &self, + workspace_id: WorkspaceId, + cx: &mut WindowContext, + ) -> Option> { + self.update(cx, |item, cx| { + cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx)) + }) + .map(|handle| Box::new(handle) as Box) + } + + fn added_to_pane( + &self, + workspace: &mut Workspace, + pane: View, + cx: &mut ViewContext, + ) { + let history = pane.read(cx).nav_history_for_item(self); + self.update(cx, |this, cx| { + this.set_nav_history(history, cx); + this.added_to_workspace(workspace, cx); + }); + + if let Some(followed_item) = self.to_followable_item_handle(cx) { + if let Some(message) = followed_item.to_state_proto(cx) { + workspace.update_followers( + followed_item.is_project_item(cx), + proto::update_followers::Variant::CreateView(proto::View { + id: followed_item + .remote_id(&workspace.app_state.client, cx) + .map(|id| id.to_proto()), + variant: Some(message), + leader_id: workspace.leader_for_pane(&pane), + }), + cx, + ); + } + } + + if workspace + .panes_by_item + .insert(self.id(), pane.downgrade()) + .is_none() + { + let mut pending_autosave = DelayedDebouncedEditAction::new(); + let pending_update = Rc::new(RefCell::new(None)); + let pending_update_scheduled = Rc::new(AtomicBool::new(false)); + + let mut event_subscription = + Some(cx.subscribe(self, move |workspace, item, event, cx| { + let pane = if let Some(pane) = workspace + .panes_by_item + .get(&item.id()) + .and_then(|pane| pane.upgrade(cx)) + { + pane + } else { + log::error!("unexpected item event after pane was dropped"); + return; + }; + + if let Some(item) = item.to_followable_item_handle(cx) { + let is_project_item = item.is_project_item(cx); + let leader_id = workspace.leader_for_pane(&pane); + + if leader_id.is_some() && item.should_unfollow_on_event(event, cx) { + workspace.unfollow(&pane, cx); + } + + if item.add_event_to_update_proto( + event, + &mut *pending_update.borrow_mut(), + cx, + ) && !pending_update_scheduled.load(Ordering::SeqCst) + { + pending_update_scheduled.store(true, Ordering::SeqCst); + cx.after_window_update({ + let pending_update = pending_update.clone(); + let pending_update_scheduled = pending_update_scheduled.clone(); + move |this, cx| { + pending_update_scheduled.store(false, Ordering::SeqCst); + this.update_followers( + is_project_item, + proto::update_followers::Variant::UpdateView( + proto::UpdateView { + id: item + .remote_id(&this.app_state.client, cx) + .map(|id| id.to_proto()), + variant: pending_update.borrow_mut().take(), + leader_id, + }, + ), + cx, + ); + } + }); + } + } + + for item_event in T::to_item_events(event).into_iter() { + match item_event { + ItemEvent::CloseItem => { + pane.update(cx, |pane, cx| { + pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx) + }) + .detach_and_log_err(cx); + return; + } + + ItemEvent::UpdateTab => { + pane.update(cx, |_, cx| { + cx.emit(pane::Event::ChangeItemTitle); + cx.notify(); + }); + } + + ItemEvent::Edit => { + let autosave = WorkspaceSettings::get_global(cx).autosave; + if let AutosaveSetting::AfterDelay { milliseconds } = autosave { + let delay = Duration::from_millis(milliseconds); + let item = item.clone(); + pending_autosave.fire_new(delay, cx, move |workspace, cx| { + Pane::autosave_item(&item, workspace.project().clone(), cx) + }); + } + } + + _ => {} + } + } + })); + + cx.observe_focus(self, move |workspace, item, focused, cx| { + if !focused + && WorkspaceSettings::get_global(cx).autosave == AutosaveSetting::OnFocusChange + { + Pane::autosave_item(&item, workspace.project.clone(), cx) + .detach_and_log_err(cx); + } + }) + .detach(); + + let item_id = self.id(); + cx.observe_release(self, move |workspace, _, _| { + workspace.panes_by_item.remove(&item_id); + event_subscription.take(); + }) + .detach(); + } + + cx.defer(|workspace, cx| { + workspace.serialize_workspace(cx); + }); + } + + fn deactivated(&self, cx: &mut WindowContext) { + self.update(cx, |this, cx| this.deactivated(cx)); + } + + fn workspace_deactivated(&self, cx: &mut WindowContext) { + self.update(cx, |this, cx| this.workspace_deactivated(cx)); + } + + fn navigate(&self, data: Box, cx: &mut WindowContext) -> bool { + self.update(cx, |this, cx| this.navigate(data, cx)) + } + + fn id(&self) -> usize { + self.id() + } + + fn window(&self) -> AnyWindowHandle { + todo!() + // AnyViewHandle::window(self) + } + + // todo!() + // fn as_any(&self) -> &AnyViewHandle { + // self + // } + + fn is_dirty(&self, cx: &AppContext) -> bool { + self.read(cx).is_dirty(cx) + } + + fn has_conflict(&self, cx: &AppContext) -> bool { + self.read(cx).has_conflict(cx) + } + + fn can_save(&self, cx: &AppContext) -> bool { + self.read(cx).can_save(cx) + } + + fn save(&self, project: Handle, cx: &mut WindowContext) -> Task> { + self.update(cx, |item, cx| item.save(project, cx)) + } + + fn save_as( + &self, + project: Handle, + abs_path: PathBuf, + cx: &mut WindowContext, + ) -> Task> { + self.update(cx, |item, cx| item.save_as(project, abs_path, cx)) + } + + fn reload(&self, project: Handle, cx: &mut WindowContext) -> Task> { + self.update(cx, |item, cx| item.reload(project, cx)) + } + + // todo!() + // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle> { + // self.read(cx).act_as_type(type_id, self, cx) + // } + + fn to_followable_item_handle(&self, cx: &AppContext) -> Option> { + if cx.has_global::() { + let builders = cx.global::(); + let item = self.as_any(); + Some(builders.get(&item.view_type())?.1(item)) + } else { + None + } + } + + fn on_release( + &self, + cx: &mut AppContext, + callback: Box, + ) -> gpui2::Subscription { + cx.observe_release(self, move |_, cx| callback(cx)) + } + + fn to_searchable_item_handle(&self, cx: &AppContext) -> Option> { + self.read(cx).as_searchable(self) + } + + fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation { + self.read(cx).breadcrumb_location() + } + + fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option> { + self.read(cx).breadcrumbs(theme, cx) + } + + fn serialized_item_kind(&self) -> Option<&'static str> { + T::serialized_item_kind() + } + + fn show_toolbar(&self, cx: &AppContext) -> bool { + self.read(cx).show_toolbar() + } + + fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option> { + self.read(cx).pixel_position_of_cursor(cx) + } +} + +// impl From> for AnyViewHandle { +// fn from(val: Box) -> Self { +// val.as_any().clone() +// } +// } + +// impl From<&Box> for AnyViewHandle { +// fn from(val: &Box) -> Self { +// val.as_any().clone() +// } +// } + +impl Clone for Box { + fn clone(&self) -> Box { + self.boxed_clone() + } +} + +// impl WeakItemHandle for WeakViewHandle { +// fn id(&self) -> usize { +// self.id() +// } + +// fn window(&self) -> AnyWindowHandle { +// self.window() +// } + +// fn upgrade(&self, cx: &AppContext) -> Option> { +// self.upgrade(cx).map(|v| Box::new(v) as Box) +// } +// } + +pub trait ProjectItem: Item { + type Item: project2::Item; + + fn for_project_item( + project: Handle, + item: Handle, + cx: &mut ViewContext, + ) -> Self + where + Self: Sized; +} + +pub trait FollowableItem: Item { + fn remote_id(&self) -> Option; + fn to_state_proto(&self, cx: &AppContext) -> Option; + fn from_state_proto( + pane: View, + project: View, + id: ViewId, + state: &mut Option, + cx: &mut AppContext, + ) -> Option>>>; + fn add_event_to_update_proto( + &self, + event: &Self::Event, + update: &mut Option, + cx: &AppContext, + ) -> bool; + fn apply_update_proto( + &mut self, + project: &Handle, + message: proto::update_view::Variant, + cx: &mut ViewContext, + ) -> Task>; + fn is_project_item(&self, cx: &AppContext) -> bool; + + fn set_leader_peer_id(&mut self, leader_peer_id: Option, cx: &mut ViewContext); + fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool; +} + +pub trait FollowableItemHandle: ItemHandle { + fn remote_id(&self, client: &Arc, cx: &AppContext) -> Option; + fn set_leader_peer_id(&self, leader_peer_id: Option, cx: &mut WindowContext); + fn to_state_proto(&self, cx: &AppContext) -> Option; + fn add_event_to_update_proto( + &self, + event: &dyn Any, + update: &mut Option, + cx: &AppContext, + ) -> bool; + fn apply_update_proto( + &self, + project: &Handle, + message: proto::update_view::Variant, + cx: &mut WindowContext, + ) -> Task>; + fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool; + fn is_project_item(&self, cx: &AppContext) -> bool; +} + +// impl FollowableItemHandle for View { +// fn remote_id(&self, client: &Arc, cx: &AppContext) -> Option { +// self.read(cx).remote_id().or_else(|| { +// client.peer_id().map(|creator| ViewId { +// creator, +// id: self.id() as u64, +// }) +// }) +// } + +// fn set_leader_peer_id(&self, leader_peer_id: Option, cx: &mut WindowContext) { +// self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx)) +// } + +// fn to_state_proto(&self, cx: &AppContext) -> Option { +// self.read(cx).to_state_proto(cx) +// } + +// fn add_event_to_update_proto( +// &self, +// event: &dyn Any, +// update: &mut Option, +// cx: &AppContext, +// ) -> bool { +// if let Some(event) = event.downcast_ref() { +// self.read(cx).add_event_to_update_proto(event, update, cx) +// } else { +// false +// } +// } + +// fn apply_update_proto( +// &self, +// project: &Handle, +// message: proto::update_view::Variant, +// cx: &mut WindowContext, +// ) -> Task> { +// self.update(cx, |this, cx| this.apply_update_proto(project, message, cx)) +// } + +// fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool { +// if let Some(event) = event.downcast_ref() { +// T::should_unfollow_on_event(event, cx) +// } else { +// false +// } +// } + +// fn is_project_item(&self, cx: &AppContext) -> bool { +// self.read(cx).is_project_item(cx) +// } +// } + +// #[cfg(any(test, feature = "test-support"))] +// pub mod test { +// use super::{Item, ItemEvent}; +// use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId}; +// use gpui2::{ +// elements::Empty, AnyElement, AppContext, Element, Entity, Handle, Task, View, +// ViewContext, View, WeakViewHandle, +// }; +// use project2::{Project, ProjectEntryId, ProjectPath, WorktreeId}; +// use smallvec::SmallVec; +// use std::{any::Any, borrow::Cow, cell::Cell, path::Path}; + +// pub struct TestProjectItem { +// pub entry_id: Option, +// pub project_path: Option, +// } + +// pub struct TestItem { +// pub workspace_id: WorkspaceId, +// pub state: String, +// pub label: String, +// pub save_count: usize, +// pub save_as_count: usize, +// pub reload_count: usize, +// pub is_dirty: bool, +// pub is_singleton: bool, +// pub has_conflict: bool, +// pub project_items: Vec>, +// pub nav_history: Option, +// pub tab_descriptions: Option>, +// pub tab_detail: Cell>, +// } + +// impl Entity for TestProjectItem { +// type Event = (); +// } + +// impl project2::Item for TestProjectItem { +// fn entry_id(&self, _: &AppContext) -> Option { +// self.entry_id +// } + +// fn project_path(&self, _: &AppContext) -> Option { +// self.project_path.clone() +// } +// } + +// pub enum TestItemEvent { +// Edit, +// } + +// impl Clone for TestItem { +// fn clone(&self) -> Self { +// Self { +// state: self.state.clone(), +// label: self.label.clone(), +// save_count: self.save_count, +// save_as_count: self.save_as_count, +// reload_count: self.reload_count, +// is_dirty: self.is_dirty, +// is_singleton: self.is_singleton, +// has_conflict: self.has_conflict, +// project_items: self.project_items.clone(), +// nav_history: None, +// tab_descriptions: None, +// tab_detail: Default::default(), +// workspace_id: self.workspace_id, +// } +// } +// } + +// impl TestProjectItem { +// pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Handle { +// let entry_id = Some(ProjectEntryId::from_proto(id)); +// let project_path = Some(ProjectPath { +// worktree_id: WorktreeId::from_usize(0), +// path: Path::new(path).into(), +// }); +// cx.add_model(|_| Self { +// entry_id, +// project_path, +// }) +// } + +// pub fn new_untitled(cx: &mut AppContext) -> Handle { +// cx.add_model(|_| Self { +// project_path: None, +// entry_id: None, +// }) +// } +// } + +// impl TestItem { +// pub fn new() -> Self { +// Self { +// state: String::new(), +// label: String::new(), +// save_count: 0, +// save_as_count: 0, +// reload_count: 0, +// is_dirty: false, +// has_conflict: false, +// project_items: Vec::new(), +// is_singleton: true, +// nav_history: None, +// tab_descriptions: None, +// tab_detail: Default::default(), +// workspace_id: 0, +// } +// } + +// pub fn new_deserialized(id: WorkspaceId) -> Self { +// let mut this = Self::new(); +// this.workspace_id = id; +// this +// } + +// pub fn with_label(mut self, state: &str) -> Self { +// self.label = state.to_string(); +// self +// } + +// pub fn with_singleton(mut self, singleton: bool) -> Self { +// self.is_singleton = singleton; +// self +// } + +// pub fn with_dirty(mut self, dirty: bool) -> Self { +// self.is_dirty = dirty; +// self +// } + +// pub fn with_conflict(mut self, has_conflict: bool) -> Self { +// self.has_conflict = has_conflict; +// self +// } + +// pub fn with_project_items(mut self, items: &[Handle]) -> Self { +// self.project_items.clear(); +// self.project_items.extend(items.iter().cloned()); +// self +// } + +// pub fn set_state(&mut self, state: String, cx: &mut ViewContext) { +// self.push_to_nav_history(cx); +// self.state = state; +// } + +// fn push_to_nav_history(&mut self, cx: &mut ViewContext) { +// if let Some(history) = &mut self.nav_history { +// history.push(Some(Box::new(self.state.clone())), cx); +// } +// } +// } + +// impl Entity for TestItem { +// type Event = TestItemEvent; +// } + +// impl View for TestItem { +// fn ui_name() -> &'static str { +// "TestItem" +// } + +// fn render(&mut self, _: &mut ViewContext) -> AnyElement { +// Empty::new().into_any() +// } +// } + +// impl Item for TestItem { +// fn tab_description(&self, detail: usize, _: &AppContext) -> Option> { +// self.tab_descriptions.as_ref().and_then(|descriptions| { +// let description = *descriptions.get(detail).or_else(|| descriptions.last())?; +// Some(description.into()) +// }) +// } + +// fn tab_content( +// &self, +// detail: Option, +// _: &theme2::Tab, +// _: &AppContext, +// ) -> AnyElement { +// self.tab_detail.set(detail); +// Empty::new().into_any() +// } + +// fn for_each_project_item( +// &self, +// cx: &AppContext, +// f: &mut dyn FnMut(usize, &dyn project2::Item), +// ) { +// self.project_items +// .iter() +// .for_each(|item| f(item.id(), item.read(cx))) +// } + +// fn is_singleton(&self, _: &AppContext) -> bool { +// self.is_singleton +// } + +// fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext) { +// self.nav_history = Some(history); +// } + +// fn navigate(&mut self, state: Box, _: &mut ViewContext) -> bool { +// let state = *state.downcast::().unwrap_or_default(); +// if state != self.state { +// self.state = state; +// true +// } else { +// false +// } +// } + +// fn deactivated(&mut self, cx: &mut ViewContext) { +// self.push_to_nav_history(cx); +// } + +// fn clone_on_split( +// &self, +// _workspace_id: WorkspaceId, +// _: &mut ViewContext, +// ) -> Option +// where +// Self: Sized, +// { +// Some(self.clone()) +// } + +// fn is_dirty(&self, _: &AppContext) -> bool { +// self.is_dirty +// } + +// fn has_conflict(&self, _: &AppContext) -> bool { +// self.has_conflict +// } + +// fn can_save(&self, cx: &AppContext) -> bool { +// !self.project_items.is_empty() +// && self +// .project_items +// .iter() +// .all(|item| item.read(cx).entry_id.is_some()) +// } + +// fn save( +// &mut self, +// _: Handle, +// _: &mut ViewContext, +// ) -> Task> { +// self.save_count += 1; +// self.is_dirty = false; +// Task::ready(Ok(())) +// } + +// fn save_as( +// &mut self, +// _: Handle, +// _: std::path::PathBuf, +// _: &mut ViewContext, +// ) -> Task> { +// self.save_as_count += 1; +// self.is_dirty = false; +// Task::ready(Ok(())) +// } + +// fn reload( +// &mut self, +// _: Handle, +// _: &mut ViewContext, +// ) -> Task> { +// self.reload_count += 1; +// self.is_dirty = false; +// Task::ready(Ok(())) +// } + +// fn to_item_events(_: &Self::Event) -> SmallVec<[ItemEvent; 2]> { +// [ItemEvent::UpdateTab, ItemEvent::Edit].into() +// } + +// fn serialized_item_kind() -> Option<&'static str> { +// Some("TestItem") +// } + +// fn deserialize( +// _project: Handle, +// _workspace: WeakViewHandle, +// workspace_id: WorkspaceId, +// _item_id: ItemId, +// cx: &mut ViewContext, +// ) -> Task>> { +// let view = cx.add_view(|_cx| Self::new_deserialized(workspace_id)); +// Task::Ready(Some(anyhow::Ok(view))) +// } +// } +// } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs new file mode 100644 index 0000000000..e0eb1b7ec2 --- /dev/null +++ b/crates/workspace2/src/pane.rs @@ -0,0 +1,2754 @@ +// mod dragged_item_receiver; + +// use super::{ItemHandle, SplitDirection}; +// pub use crate::toolbar::Toolbar; +// use crate::{ +// item::{ItemSettings, WeakItemHandle}, +// notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile, NewSearch, ToggleZoom, +// Workspace, WorkspaceSettings, +// }; +// use anyhow::Result; +// use collections::{HashMap, HashSet, VecDeque}; +// // use context_menu::{ContextMenu, ContextMenuItem}; + +// use dragged_item_receiver::dragged_item_receiver; +// use fs2::repository::GitFileStatus; +// use futures::StreamExt; +// use gpui2::{ +// actions, +// elements::*, +// geometry::{ +// rect::RectF, +// vector::{vec2f, Vector2F}, +// }, +// impl_actions, +// keymap_matcher::KeymapContext, +// platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel}, +// Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, +// ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle, +// WindowContext, +// }; +// use project2::{Project, ProjectEntryId, ProjectPath}; +use serde::Deserialize; +// use std::{ +// any::Any, +// cell::RefCell, +// cmp, mem, +// path::{Path, PathBuf}, +// rc::Rc, +// sync::{ +// atomic::{AtomicUsize, Ordering}, +// Arc, +// }, +// }; +// use theme2::{Theme, ThemeSettings}; +// use util::truncate_and_remove_front; + +#[derive(PartialEq, Clone, Copy, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub enum SaveIntent { + /// write all files (even if unchanged) + /// prompt before overwriting on-disk changes + Save, + /// write any files that have local changes + /// prompt before overwriting on-disk changes + SaveAll, + /// always prompt for a new path + SaveAs, + /// prompt "you have unsaved changes" before writing + Close, + /// write all dirty files, don't prompt on conflict + Overwrite, + /// skip all save-related behavior + Skip, +} + +// #[derive(Clone, Deserialize, PartialEq)] +// pub struct ActivateItem(pub usize); + +// #[derive(Clone, PartialEq)] +// pub struct CloseItemById { +// pub item_id: usize, +// pub pane: WeakViewHandle, +// } + +// #[derive(Clone, PartialEq)] +// pub struct CloseItemsToTheLeftById { +// pub item_id: usize, +// pub pane: WeakViewHandle, +// } + +// #[derive(Clone, PartialEq)] +// pub struct CloseItemsToTheRightById { +// pub item_id: usize, +// pub pane: WeakViewHandle, +// } + +// #[derive(Clone, PartialEq, Debug, Deserialize, Default)] +// #[serde(rename_all = "camelCase")] +// pub struct CloseActiveItem { +// pub save_intent: Option, +// } + +// #[derive(Clone, PartialEq, Debug, Deserialize)] +// #[serde(rename_all = "camelCase")] +// pub struct CloseAllItems { +// pub save_intent: Option, +// } + +// actions!( +// pane, +// [ +// ActivatePrevItem, +// ActivateNextItem, +// ActivateLastItem, +// CloseInactiveItems, +// CloseCleanItems, +// CloseItemsToTheLeft, +// CloseItemsToTheRight, +// GoBack, +// GoForward, +// ReopenClosedItem, +// SplitLeft, +// SplitUp, +// SplitRight, +// SplitDown, +// ] +// ); + +// impl_actions!(pane, [ActivateItem, CloseActiveItem, CloseAllItems]); + +// const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; + +// pub fn init(cx: &mut AppContext) { +// cx.add_action(Pane::toggle_zoom); +// cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { +// pane.activate_item(action.0, true, true, cx); +// }); +// cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| { +// pane.activate_item(pane.items.len() - 1, true, true, cx); +// }); +// cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { +// pane.activate_prev_item(true, cx); +// }); +// cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| { +// pane.activate_next_item(true, cx); +// }); +// cx.add_async_action(Pane::close_active_item); +// cx.add_async_action(Pane::close_inactive_items); +// cx.add_async_action(Pane::close_clean_items); +// cx.add_async_action(Pane::close_items_to_the_left); +// cx.add_async_action(Pane::close_items_to_the_right); +// cx.add_async_action(Pane::close_all_items); +// cx.add_action(|pane: &mut Pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx)); +// cx.add_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)); +// cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx)); +// cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)); +// } + +#[derive(Debug)] +pub enum Event { + AddItem { item: Box }, + ActivateItem { local: bool }, + Remove, + RemoveItem { item_id: usize }, + Split(SplitDirection), + ChangeItemTitle, + Focus, + ZoomIn, + ZoomOut, +} + +use crate::{ + item::{ItemHandle, WeakItemHandle}, + SplitDirection, +}; +use collections::{HashMap, VecDeque}; +use gpui2::{Handle, ViewContext, WeakView}; +use project2::{Project, ProjectEntryId, ProjectPath}; +use std::{ + any::Any, + cell::RefCell, + cmp, mem, + path::PathBuf, + rc::Rc, + sync::{atomic::AtomicUsize, Arc}, +}; + +pub struct Pane { + items: Vec>, + // activation_history: Vec, + // zoomed: bool, + // active_item_index: usize, + // last_focused_view_by_item: HashMap, + // autoscroll: bool, + nav_history: NavHistory, + // toolbar: ViewHandle, + // tab_bar_context_menu: TabBarContextMenu, + // tab_context_menu: ViewHandle, + // workspace: WeakViewHandle, + project: Handle, + // has_focus: bool, + // can_drop: Rc, &WindowContext) -> bool>, + // can_split: bool, + // render_tab_bar_buttons: Rc) -> AnyElement>, +} + +pub struct ItemNavHistory { + history: NavHistory, + item: Rc, +} + +#[derive(Clone)] +pub struct NavHistory(Rc>); + +struct NavHistoryState { + mode: NavigationMode, + backward_stack: VecDeque, + forward_stack: VecDeque, + closed_stack: VecDeque, + paths_by_item: HashMap)>, + pane: WeakView, + next_timestamp: Arc, +} + +#[derive(Copy, Clone)] +pub enum NavigationMode { + Normal, + GoingBack, + GoingForward, + ClosingItem, + ReopeningClosedItem, + Disabled, +} + +impl Default for NavigationMode { + fn default() -> Self { + Self::Normal + } +} + +pub struct NavigationEntry { + pub item: Rc, + pub data: Option>, + pub timestamp: usize, +} + +// pub struct DraggedItem { +// pub handle: Box, +// pub pane: WeakViewHandle, +// } + +// pub enum ReorderBehavior { +// None, +// MoveAfterActive, +// MoveToIndex(usize), +// } + +// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +// enum TabBarContextMenuKind { +// New, +// Split, +// } + +// struct TabBarContextMenu { +// kind: TabBarContextMenuKind, +// handle: ViewHandle, +// } + +// impl TabBarContextMenu { +// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option> { +// if self.kind == kind { +// return Some(self.handle.clone()); +// } +// None +// } +// } + +// #[allow(clippy::too_many_arguments)] +// fn nav_button)>( +// svg_path: &'static str, +// style: theme2::Interactive, +// nav_button_height: f32, +// tooltip_style: TooltipStyle, +// enabled: bool, +// on_click: F, +// tooltip_action: A, +// action_name: &str, +// cx: &mut ViewContext, +// ) -> AnyElement { +// MouseEventHandler::new::(0, cx, |state, _| { +// let style = if enabled { +// style.style_for(state) +// } else { +// style.disabled_style() +// }; +// Svg::new(svg_path) +// .with_color(style.color) +// .constrained() +// .with_width(style.icon_width) +// .aligned() +// .contained() +// .with_style(style.container) +// .constrained() +// .with_width(style.button_width) +// .with_height(nav_button_height) +// .aligned() +// .top() +// }) +// .with_cursor_style(if enabled { +// CursorStyle::PointingHand +// } else { +// CursorStyle::default() +// }) +// .on_click(MouseButton::Left, move |_, toolbar, cx| { +// on_click(toolbar, cx) +// }) +// .with_tooltip::( +// 0, +// action_name.to_string(), +// Some(Box::new(tooltip_action)), +// tooltip_style, +// cx, +// ) +// .contained() +// .into_any_named("nav button") +// } + +impl Pane { + // pub fn new( + // workspace: WeakViewHandle, + // project: ModelHandle, + // next_timestamp: Arc, + // cx: &mut ViewContext, + // ) -> Self { + // let pane_view_id = cx.view_id(); + // let handle = cx.weak_handle(); + // let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)); + // context_menu.update(cx, |menu, _| { + // menu.set_position_mode(OverlayPositionMode::Local) + // }); + + // Self { + // items: Vec::new(), + // activation_history: Vec::new(), + // zoomed: false, + // active_item_index: 0, + // last_focused_view_by_item: Default::default(), + // autoscroll: false, + // nav_history: NavHistory(Rc::new(RefCell::new(NavHistoryState { + // mode: NavigationMode::Normal, + // backward_stack: Default::default(), + // forward_stack: Default::default(), + // closed_stack: Default::default(), + // paths_by_item: Default::default(), + // pane: handle.clone(), + // next_timestamp, + // }))), + // toolbar: cx.add_view(|_| Toolbar::new()), + // tab_bar_context_menu: TabBarContextMenu { + // kind: TabBarContextMenuKind::New, + // handle: context_menu, + // }, + // tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)), + // workspace, + // project, + // has_focus: false, + // can_drop: Rc::new(|_, _| true), + // can_split: true, + // render_tab_bar_buttons: Rc::new(move |pane, cx| { + // Flex::row() + // // New menu + // .with_child(Self::render_tab_bar_button( + // 0, + // "icons/plus.svg", + // false, + // Some(("New...".into(), None)), + // cx, + // |pane, cx| pane.deploy_new_menu(cx), + // |pane, cx| { + // pane.tab_bar_context_menu + // .handle + // .update(cx, |menu, _| menu.delay_cancel()) + // }, + // pane.tab_bar_context_menu + // .handle_if_kind(TabBarContextMenuKind::New), + // )) + // .with_child(Self::render_tab_bar_button( + // 1, + // "icons/split.svg", + // false, + // Some(("Split Pane".into(), None)), + // cx, + // |pane, cx| pane.deploy_split_menu(cx), + // |pane, cx| { + // pane.tab_bar_context_menu + // .handle + // .update(cx, |menu, _| menu.delay_cancel()) + // }, + // pane.tab_bar_context_menu + // .handle_if_kind(TabBarContextMenuKind::Split), + // )) + // .with_child({ + // let icon_path; + // let tooltip_label; + // if pane.is_zoomed() { + // icon_path = "icons/minimize.svg"; + // tooltip_label = "Zoom In"; + // } else { + // icon_path = "icons/maximize.svg"; + // tooltip_label = "Zoom In"; + // } + + // Pane::render_tab_bar_button( + // 2, + // icon_path, + // pane.is_zoomed(), + // Some((tooltip_label, Some(Box::new(ToggleZoom)))), + // cx, + // move |pane, cx| pane.toggle_zoom(&Default::default(), cx), + // move |_, _| {}, + // None, + // ) + // }) + // .into_any() + // }), + // } + // } + + // pub(crate) fn workspace(&self) -> &WeakViewHandle { + // &self.workspace + // } + + // pub fn has_focus(&self) -> bool { + // self.has_focus + // } + + // pub fn active_item_index(&self) -> usize { + // self.active_item_index + // } + + // pub fn on_can_drop(&mut self, can_drop: F) + // where + // F: 'static + Fn(&DragAndDrop, &WindowContext) -> bool, + // { + // self.can_drop = Rc::new(can_drop); + // } + + // pub fn set_can_split(&mut self, can_split: bool, cx: &mut ViewContext) { + // self.can_split = can_split; + // cx.notify(); + // } + + // pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut ViewContext) { + // self.toolbar.update(cx, |toolbar, cx| { + // toolbar.set_can_navigate(can_navigate, cx); + // }); + // cx.notify(); + // } + + // pub fn set_render_tab_bar_buttons(&mut self, cx: &mut ViewContext, render: F) + // where + // F: 'static + Fn(&mut Pane, &mut ViewContext) -> AnyElement, + // { + // self.render_tab_bar_buttons = Rc::new(render); + // cx.notify(); + // } + + // pub fn nav_history_for_item(&self, item: &ViewHandle) -> ItemNavHistory { + // ItemNavHistory { + // history: self.nav_history.clone(), + // item: Rc::new(item.downgrade()), + // } + // } + + // pub fn nav_history(&self) -> &NavHistory { + // &self.nav_history + // } + + // pub fn nav_history_mut(&mut self) -> &mut NavHistory { + // &mut self.nav_history + // } + + // pub fn disable_history(&mut self) { + // self.nav_history.disable(); + // } + + // pub fn enable_history(&mut self) { + // self.nav_history.enable(); + // } + + // pub fn can_navigate_backward(&self) -> bool { + // !self.nav_history.0.borrow().backward_stack.is_empty() + // } + + // pub fn can_navigate_forward(&self) -> bool { + // !self.nav_history.0.borrow().forward_stack.is_empty() + // } + + // fn history_updated(&mut self, cx: &mut ViewContext) { + // self.toolbar.update(cx, |_, cx| cx.notify()); + // } + + pub(crate) fn open_item( + &mut self, + project_entry_id: ProjectEntryId, + focus_item: bool, + cx: &mut ViewContext, + build_item: impl FnOnce(&mut ViewContext) -> Box, + ) -> Box { + let mut existing_item = None; + for (index, item) in self.items.iter().enumerate() { + if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [project_entry_id] + { + let item = item.boxed_clone(); + existing_item = Some((index, item)); + break; + } + } + + if let Some((index, existing_item)) = existing_item { + self.activate_item(index, focus_item, focus_item, cx); + existing_item + } else { + let new_item = build_item(cx); + self.add_item(new_item.clone(), true, focus_item, None, cx); + new_item + } + } + + pub fn add_item( + &mut self, + item: Box, + activate_pane: bool, + focus_item: bool, + destination_index: Option, + cx: &mut ViewContext, + ) { + if item.is_singleton(cx) { + if let Some(&entry_id) = item.project_entry_ids(cx).get(0) { + let project = self.project.read(cx); + if let Some(project_path) = project.path_for_entry(entry_id, cx) { + let abs_path = project.absolute_path(&project_path, cx); + self.nav_history + .0 + .borrow_mut() + .paths_by_item + .insert(item.id(), (project_path, abs_path)); + } + } + } + // If no destination index is specified, add or move the item after the active item. + let mut insertion_index = { + cmp::min( + if let Some(destination_index) = destination_index { + destination_index + } else { + self.active_item_index + 1 + }, + self.items.len(), + ) + }; + + // Does the item already exist? + let project_entry_id = if item.is_singleton(cx) { + item.project_entry_ids(cx).get(0).copied() + } else { + None + }; + + let existing_item_index = self.items.iter().position(|existing_item| { + if existing_item.id() == item.id() { + true + } else if existing_item.is_singleton(cx) { + existing_item + .project_entry_ids(cx) + .get(0) + .map_or(false, |existing_entry_id| { + Some(existing_entry_id) == project_entry_id.as_ref() + }) + } else { + false + } + }); + + if let Some(existing_item_index) = existing_item_index { + // If the item already exists, move it to the desired destination and activate it + + if existing_item_index != insertion_index { + let existing_item_is_active = existing_item_index == self.active_item_index; + + // If the caller didn't specify a destination and the added item is already + // the active one, don't move it + if existing_item_is_active && destination_index.is_none() { + insertion_index = existing_item_index; + } else { + self.items.remove(existing_item_index); + if existing_item_index < self.active_item_index { + self.active_item_index -= 1; + } + insertion_index = insertion_index.min(self.items.len()); + + self.items.insert(insertion_index, item.clone()); + + if existing_item_is_active { + self.active_item_index = insertion_index; + } else if insertion_index <= self.active_item_index { + self.active_item_index += 1; + } + } + + cx.notify(); + } + + self.activate_item(insertion_index, activate_pane, focus_item, cx); + } else { + self.items.insert(insertion_index, item.clone()); + if insertion_index <= self.active_item_index { + self.active_item_index += 1; + } + + self.activate_item(insertion_index, activate_pane, focus_item, cx); + cx.notify(); + } + + cx.emit(Event::AddItem { item }); + } + + // pub fn items_len(&self) -> usize { + // self.items.len() + // } + + // pub fn items(&self) -> impl Iterator> + DoubleEndedIterator { + // self.items.iter() + // } + + // pub fn items_of_type(&self) -> impl '_ + Iterator> { + // self.items + // .iter() + // .filter_map(|item| item.as_any().clone().downcast()) + // } + + // pub fn active_item(&self) -> Option> { + // self.items.get(self.active_item_index).cloned() + // } + + // pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option { + // self.items + // .get(self.active_item_index)? + // .pixel_position_of_cursor(cx) + // } + + // pub fn item_for_entry( + // &self, + // entry_id: ProjectEntryId, + // cx: &AppContext, + // ) -> Option> { + // self.items.iter().find_map(|item| { + // if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] { + // Some(item.boxed_clone()) + // } else { + // None + // } + // }) + // } + + // pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option { + // self.items.iter().position(|i| i.id() == item.id()) + // } + + // pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + // // Potentially warn the user of the new keybinding + // let workspace_handle = self.workspace().clone(); + // cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) + // .detach(); + + // if self.zoomed { + // cx.emit(Event::ZoomOut); + // } else if !self.items.is_empty() { + // if !self.has_focus { + // cx.focus_self(); + // } + // cx.emit(Event::ZoomIn); + // } + // } + + pub fn activate_item( + &mut self, + index: usize, + activate_pane: bool, + focus_item: bool, + cx: &mut ViewContext, + ) { + use NavigationMode::{GoingBack, GoingForward}; + + if index < self.items.len() { + let prev_active_item_ix = mem::replace(&mut self.active_item_index, index); + if prev_active_item_ix != self.active_item_index + || matches!(self.nav_history.mode(), GoingBack | GoingForward) + { + if let Some(prev_item) = self.items.get(prev_active_item_ix) { + prev_item.deactivated(cx); + } + + cx.emit(Event::ActivateItem { + local: activate_pane, + }); + } + + if let Some(newly_active_item) = self.items.get(index) { + self.activation_history + .retain(|&previously_active_item_id| { + previously_active_item_id != newly_active_item.id() + }); + self.activation_history.push(newly_active_item.id()); + } + + self.update_toolbar(cx); + + if focus_item { + self.focus_active_item(cx); + } + + self.autoscroll = true; + cx.notify(); + } + } + + // pub fn activate_prev_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { + // let mut index = self.active_item_index; + // if index > 0 { + // index -= 1; + // } else if !self.items.is_empty() { + // index = self.items.len() - 1; + // } + // self.activate_item(index, activate_pane, activate_pane, cx); + // } + + // pub fn activate_next_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { + // let mut index = self.active_item_index; + // if index + 1 < self.items.len() { + // index += 1; + // } else { + // index = 0; + // } + // self.activate_item(index, activate_pane, activate_pane, cx); + // } + + // pub fn close_active_item( + // &mut self, + // action: &CloseActiveItem, + // cx: &mut ViewContext, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; + // } + // let active_item_id = self.items[self.active_item_index].id(); + // Some(self.close_item_by_id( + // active_item_id, + // action.save_intent.unwrap_or(SaveIntent::Close), + // cx, + // )) + // } + + // pub fn close_item_by_id( + // &mut self, + // item_id_to_close: usize, + // save_intent: SaveIntent, + // cx: &mut ViewContext, + // ) -> Task> { + // self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close) + // } + + // pub fn close_inactive_items( + // &mut self, + // _: &CloseInactiveItems, + // cx: &mut ViewContext, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; + // } + + // let active_item_id = self.items[self.active_item_index].id(); + // Some(self.close_items(cx, SaveIntent::Close, move |item_id| { + // item_id != active_item_id + // })) + // } + + // pub fn close_clean_items( + // &mut self, + // _: &CloseCleanItems, + // cx: &mut ViewContext, + // ) -> Option>> { + // let item_ids: Vec<_> = self + // .items() + // .filter(|item| !item.is_dirty(cx)) + // .map(|item| item.id()) + // .collect(); + // Some(self.close_items(cx, SaveIntent::Close, move |item_id| { + // item_ids.contains(&item_id) + // })) + // } + + // pub fn close_items_to_the_left( + // &mut self, + // _: &CloseItemsToTheLeft, + // cx: &mut ViewContext, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; + // } + // let active_item_id = self.items[self.active_item_index].id(); + // Some(self.close_items_to_the_left_by_id(active_item_id, cx)) + // } + + // pub fn close_items_to_the_left_by_id( + // &mut self, + // item_id: usize, + // cx: &mut ViewContext, + // ) -> Task> { + // let item_ids: Vec<_> = self + // .items() + // .take_while(|item| item.id() != item_id) + // .map(|item| item.id()) + // .collect(); + // self.close_items(cx, SaveIntent::Close, move |item_id| { + // item_ids.contains(&item_id) + // }) + // } + + // pub fn close_items_to_the_right( + // &mut self, + // _: &CloseItemsToTheRight, + // cx: &mut ViewContext, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; + // } + // let active_item_id = self.items[self.active_item_index].id(); + // Some(self.close_items_to_the_right_by_id(active_item_id, cx)) + // } + + // pub fn close_items_to_the_right_by_id( + // &mut self, + // item_id: usize, + // cx: &mut ViewContext, + // ) -> Task> { + // let item_ids: Vec<_> = self + // .items() + // .rev() + // .take_while(|item| item.id() != item_id) + // .map(|item| item.id()) + // .collect(); + // self.close_items(cx, SaveIntent::Close, move |item_id| { + // item_ids.contains(&item_id) + // }) + // } + + // pub fn close_all_items( + // &mut self, + // action: &CloseAllItems, + // cx: &mut ViewContext, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; + // } + + // Some( + // self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| { + // true + // }), + // ) + // } + + // pub(super) fn file_names_for_prompt( + // items: &mut dyn Iterator>, + // all_dirty_items: usize, + // cx: &AppContext, + // ) -> String { + // /// Quantity of item paths displayed in prompt prior to cutoff.. + // const FILE_NAMES_CUTOFF_POINT: usize = 10; + // let mut file_names: Vec<_> = items + // .filter_map(|item| { + // item.project_path(cx).and_then(|project_path| { + // project_path + // .path + // .file_name() + // .and_then(|name| name.to_str().map(ToOwned::to_owned)) + // }) + // }) + // .take(FILE_NAMES_CUTOFF_POINT) + // .collect(); + // let should_display_followup_text = + // all_dirty_items > FILE_NAMES_CUTOFF_POINT || file_names.len() != all_dirty_items; + // if should_display_followup_text { + // let not_shown_files = all_dirty_items - file_names.len(); + // if not_shown_files == 1 { + // file_names.push(".. 1 file not shown".into()); + // } else { + // file_names.push(format!(".. {} files not shown", not_shown_files).into()); + // } + // } + // let file_names = file_names.join("\n"); + // format!( + // "Do you want to save changes to the following {} files?\n{file_names}", + // all_dirty_items + // ) + // } + + // pub fn close_items( + // &mut self, + // cx: &mut ViewContext, + // mut save_intent: SaveIntent, + // should_close: impl 'static + Fn(usize) -> bool, + // ) -> Task> { + // // Find the items to close. + // let mut items_to_close = Vec::new(); + // let mut dirty_items = Vec::new(); + // for item in &self.items { + // if should_close(item.id()) { + // items_to_close.push(item.boxed_clone()); + // if item.is_dirty(cx) { + // dirty_items.push(item.boxed_clone()); + // } + // } + // } + + // // If a buffer is open both in a singleton editor and in a multibuffer, make sure + // // to focus the singleton buffer when prompting to save that buffer, as opposed + // // to focusing the multibuffer, because this gives the user a more clear idea + // // of what content they would be saving. + // items_to_close.sort_by_key(|item| !item.is_singleton(cx)); + + // let workspace = self.workspace.clone(); + // cx.spawn(|pane, mut cx| async move { + // if save_intent == SaveIntent::Close && dirty_items.len() > 1 { + // let mut answer = pane.update(&mut cx, |_, cx| { + // let prompt = + // Self::file_names_for_prompt(&mut dirty_items.iter(), dirty_items.len(), cx); + // cx.prompt( + // PromptLevel::Warning, + // &prompt, + // &["Save all", "Discard all", "Cancel"], + // ) + // })?; + // match answer.next().await { + // Some(0) => save_intent = SaveIntent::SaveAll, + // Some(1) => save_intent = SaveIntent::Skip, + // _ => {} + // } + // } + // let mut saved_project_items_ids = HashSet::default(); + // for item in items_to_close.clone() { + // // Find the item's current index and its set of project item models. Avoid + // // storing these in advance, in case they have changed since this task + // // was started. + // let (item_ix, mut project_item_ids) = pane.read_with(&cx, |pane, cx| { + // (pane.index_for_item(&*item), item.project_item_model_ids(cx)) + // })?; + // let item_ix = if let Some(ix) = item_ix { + // ix + // } else { + // continue; + // }; + + // // Check if this view has any project items that are not open anywhere else + // // in the workspace, AND that the user has not already been prompted to save. + // // If there are any such project entries, prompt the user to save this item. + // let project = workspace.read_with(&cx, |workspace, cx| { + // for item in workspace.items(cx) { + // if !items_to_close + // .iter() + // .any(|item_to_close| item_to_close.id() == item.id()) + // { + // let other_project_item_ids = item.project_item_model_ids(cx); + // project_item_ids.retain(|id| !other_project_item_ids.contains(id)); + // } + // } + // workspace.project().clone() + // })?; + // let should_save = project_item_ids + // .iter() + // .any(|id| saved_project_items_ids.insert(*id)); + + // if should_save + // && !Self::save_item( + // project.clone(), + // &pane, + // item_ix, + // &*item, + // save_intent, + // &mut cx, + // ) + // .await? + // { + // break; + // } + + // // Remove the item from the pane. + // pane.update(&mut cx, |pane, cx| { + // if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) { + // pane.remove_item(item_ix, false, cx); + // } + // })?; + // } + + // pane.update(&mut cx, |_, cx| cx.notify())?; + // Ok(()) + // }) + // } + + // pub fn remove_item( + // &mut self, + // item_index: usize, + // activate_pane: bool, + // cx: &mut ViewContext, + // ) { + // self.activation_history + // .retain(|&history_entry| history_entry != self.items[item_index].id()); + + // if item_index == self.active_item_index { + // let index_to_activate = self + // .activation_history + // .pop() + // .and_then(|last_activated_item| { + // self.items.iter().enumerate().find_map(|(index, item)| { + // (item.id() == last_activated_item).then_some(index) + // }) + // }) + // // We didn't have a valid activation history entry, so fallback + // // to activating the item to the left + // .unwrap_or_else(|| item_index.min(self.items.len()).saturating_sub(1)); + + // let should_activate = activate_pane || self.has_focus; + // self.activate_item(index_to_activate, should_activate, should_activate, cx); + // } + + // let item = self.items.remove(item_index); + + // cx.emit(Event::RemoveItem { item_id: item.id() }); + // if self.items.is_empty() { + // item.deactivated(cx); + // self.update_toolbar(cx); + // cx.emit(Event::Remove); + // } + + // if item_index < self.active_item_index { + // self.active_item_index -= 1; + // } + + // self.nav_history.set_mode(NavigationMode::ClosingItem); + // item.deactivated(cx); + // self.nav_history.set_mode(NavigationMode::Normal); + + // if let Some(path) = item.project_path(cx) { + // let abs_path = self + // .nav_history + // .0 + // .borrow() + // .paths_by_item + // .get(&item.id()) + // .and_then(|(_, abs_path)| abs_path.clone()); + + // self.nav_history + // .0 + // .borrow_mut() + // .paths_by_item + // .insert(item.id(), (path, abs_path)); + // } else { + // self.nav_history + // .0 + // .borrow_mut() + // .paths_by_item + // .remove(&item.id()); + // } + + // if self.items.is_empty() && self.zoomed { + // cx.emit(Event::ZoomOut); + // } + + // cx.notify(); + // } + + // pub async fn save_item( + // project: ModelHandle, + // pane: &WeakViewHandle, + // item_ix: usize, + // item: &dyn ItemHandle, + // save_intent: SaveIntent, + // cx: &mut AsyncAppContext, + // ) -> Result { + // const CONFLICT_MESSAGE: &str = + // "This file has changed on disk since you started editing it. Do you want to overwrite it?"; + + // if save_intent == SaveIntent::Skip { + // return Ok(true); + // } + + // let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.read(|cx| { + // ( + // item.has_conflict(cx), + // item.is_dirty(cx), + // item.can_save(cx), + // item.is_singleton(cx), + // ) + // }); + + // // when saving a single buffer, we ignore whether or not it's dirty. + // if save_intent == SaveIntent::Save { + // is_dirty = true; + // } + + // if save_intent == SaveIntent::SaveAs { + // is_dirty = true; + // has_conflict = false; + // can_save = false; + // } + + // if save_intent == SaveIntent::Overwrite { + // has_conflict = false; + // } + + // if has_conflict && can_save { + // let mut answer = pane.update(cx, |pane, cx| { + // pane.activate_item(item_ix, true, true, cx); + // cx.prompt( + // PromptLevel::Warning, + // CONFLICT_MESSAGE, + // &["Overwrite", "Discard", "Cancel"], + // ) + // })?; + // match answer.next().await { + // Some(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?, + // Some(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?, + // _ => return Ok(false), + // } + // } else if is_dirty && (can_save || can_save_as) { + // if save_intent == SaveIntent::Close { + // let will_autosave = cx.read(|cx| { + // matches!( + // settings::get::(cx).autosave, + // AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange + // ) && Self::can_autosave_item(&*item, cx) + // }); + // if !will_autosave { + // let mut answer = pane.update(cx, |pane, cx| { + // pane.activate_item(item_ix, true, true, cx); + // let prompt = dirty_message_for(item.project_path(cx)); + // cx.prompt( + // PromptLevel::Warning, + // &prompt, + // &["Save", "Don't Save", "Cancel"], + // ) + // })?; + // match answer.next().await { + // Some(0) => {} + // Some(1) => return Ok(true), // Don't save his file + // _ => return Ok(false), // Cancel + // } + // } + // } + + // if can_save { + // pane.update(cx, |_, cx| item.save(project, cx))?.await?; + // } else if can_save_as { + // let start_abs_path = project + // .read_with(cx, |project, cx| { + // let worktree = project.visible_worktrees(cx).next()?; + // Some(worktree.read(cx).as_local()?.abs_path().to_path_buf()) + // }) + // .unwrap_or_else(|| Path::new("").into()); + + // let mut abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path)); + // if let Some(abs_path) = abs_path.next().await.flatten() { + // pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))? + // .await?; + // } else { + // return Ok(false); + // } + // } + // } + // Ok(true) + // } + + // fn can_autosave_item(item: &dyn ItemHandle, cx: &AppContext) -> bool { + // let is_deleted = item.project_entry_ids(cx).is_empty(); + // item.is_dirty(cx) && !item.has_conflict(cx) && item.can_save(cx) && !is_deleted + // } + + // pub fn autosave_item( + // item: &dyn ItemHandle, + // project: ModelHandle, + // cx: &mut WindowContext, + // ) -> Task> { + // if Self::can_autosave_item(item, cx) { + // item.save(project, cx) + // } else { + // Task::ready(Ok(())) + // } + // } + + // pub fn focus_active_item(&mut self, cx: &mut ViewContext) { + // if let Some(active_item) = self.active_item() { + // cx.focus(active_item.as_any()); + // } + // } + + // pub fn split(&mut self, direction: SplitDirection, cx: &mut ViewContext) { + // cx.emit(Event::Split(direction)); + // } + + // fn deploy_split_menu(&mut self, cx: &mut ViewContext) { + // self.tab_bar_context_menu.handle.update(cx, |menu, cx| { + // menu.toggle( + // Default::default(), + // AnchorCorner::TopRight, + // vec![ + // ContextMenuItem::action("Split Right", SplitRight), + // ContextMenuItem::action("Split Left", SplitLeft), + // ContextMenuItem::action("Split Up", SplitUp), + // ContextMenuItem::action("Split Down", SplitDown), + // ], + // cx, + // ); + // }); + + // self.tab_bar_context_menu.kind = TabBarContextMenuKind::Split; + // } + + // fn deploy_new_menu(&mut self, cx: &mut ViewContext) { + // self.tab_bar_context_menu.handle.update(cx, |menu, cx| { + // menu.toggle( + // Default::default(), + // AnchorCorner::TopRight, + // vec![ + // ContextMenuItem::action("New File", NewFile), + // ContextMenuItem::action("New Terminal", NewCenterTerminal), + // ContextMenuItem::action("New Search", NewSearch), + // ], + // cx, + // ); + // }); + + // self.tab_bar_context_menu.kind = TabBarContextMenuKind::New; + // } + + // fn deploy_tab_context_menu( + // &mut self, + // position: Vector2F, + // target_item_id: usize, + // cx: &mut ViewContext, + // ) { + // let active_item_id = self.items[self.active_item_index].id(); + // let is_active_item = target_item_id == active_item_id; + // let target_pane = cx.weak_handle(); + + // // The `CloseInactiveItems` action should really be called "CloseOthers" and the behaviour should be dynamically based on the tab the action is ran on. Currently, this is a weird action because you can run it on a non-active tab and it will close everything by the actual active tab + + // self.tab_context_menu.update(cx, |menu, cx| { + // menu.show( + // position, + // AnchorCorner::TopLeft, + // if is_active_item { + // vec![ + // ContextMenuItem::action( + // "Close Active Item", + // CloseActiveItem { save_intent: None }, + // ), + // ContextMenuItem::action("Close Inactive Items", CloseInactiveItems), + // ContextMenuItem::action("Close Clean Items", CloseCleanItems), + // ContextMenuItem::action("Close Items To The Left", CloseItemsToTheLeft), + // ContextMenuItem::action("Close Items To The Right", CloseItemsToTheRight), + // ContextMenuItem::action( + // "Close All Items", + // CloseAllItems { save_intent: None }, + // ), + // ] + // } else { + // // In the case of the user right clicking on a non-active tab, for some item-closing commands, we need to provide the id of the tab, for the others, we can reuse the existing command. + // vec![ + // ContextMenuItem::handler("Close Inactive Item", { + // let pane = target_pane.clone(); + // move |cx| { + // if let Some(pane) = pane.upgrade(cx) { + // pane.update(cx, |pane, cx| { + // pane.close_item_by_id( + // target_item_id, + // SaveIntent::Close, + // cx, + // ) + // .detach_and_log_err(cx); + // }) + // } + // } + // }), + // ContextMenuItem::action("Close Inactive Items", CloseInactiveItems), + // ContextMenuItem::action("Close Clean Items", CloseCleanItems), + // ContextMenuItem::handler("Close Items To The Left", { + // let pane = target_pane.clone(); + // move |cx| { + // if let Some(pane) = pane.upgrade(cx) { + // pane.update(cx, |pane, cx| { + // pane.close_items_to_the_left_by_id(target_item_id, cx) + // .detach_and_log_err(cx); + // }) + // } + // } + // }), + // ContextMenuItem::handler("Close Items To The Right", { + // let pane = target_pane.clone(); + // move |cx| { + // if let Some(pane) = pane.upgrade(cx) { + // pane.update(cx, |pane, cx| { + // pane.close_items_to_the_right_by_id(target_item_id, cx) + // .detach_and_log_err(cx); + // }) + // } + // } + // }), + // ContextMenuItem::action( + // "Close All Items", + // CloseAllItems { save_intent: None }, + // ), + // ] + // }, + // cx, + // ); + // }); + // } + + // pub fn toolbar(&self) -> &ViewHandle { + // &self.toolbar + // } + + // pub fn handle_deleted_project_item( + // &mut self, + // entry_id: ProjectEntryId, + // cx: &mut ViewContext, + // ) -> Option<()> { + // let (item_index_to_delete, item_id) = self.items().enumerate().find_map(|(i, item)| { + // if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] { + // Some((i, item.id())) + // } else { + // None + // } + // })?; + + // self.remove_item(item_index_to_delete, false, cx); + // self.nav_history.remove_item(item_id); + + // Some(()) + // } + + // fn update_toolbar(&mut self, cx: &mut ViewContext) { + // let active_item = self + // .items + // .get(self.active_item_index) + // .map(|item| item.as_ref()); + // self.toolbar.update(cx, |toolbar, cx| { + // toolbar.set_active_item(active_item, cx); + // }); + // } + + // fn render_tabs(&mut self, cx: &mut ViewContext) -> impl Element { + // let theme = theme::current(cx).clone(); + + // let pane = cx.handle().downgrade(); + // let autoscroll = if mem::take(&mut self.autoscroll) { + // Some(self.active_item_index) + // } else { + // None + // }; + + // let pane_active = self.has_focus; + + // enum Tabs {} + // let mut row = Flex::row().scrollable::(1, autoscroll, cx); + // for (ix, (item, detail)) in self + // .items + // .iter() + // .cloned() + // .zip(self.tab_details(cx)) + // .enumerate() + // { + // let git_status = item + // .project_path(cx) + // .and_then(|path| self.project.read(cx).entry_for_path(&path, cx)) + // .and_then(|entry| entry.git_status()); + + // let detail = if detail == 0 { None } else { Some(detail) }; + // let tab_active = ix == self.active_item_index; + + // row.add_child({ + // enum TabDragReceiver {} + // let mut receiver = + // dragged_item_receiver::(self, ix, ix, true, None, cx, { + // let item = item.clone(); + // let pane = pane.clone(); + // let detail = detail.clone(); + + // let theme = theme::current(cx).clone(); + // let mut tooltip_theme = theme.tooltip.clone(); + // tooltip_theme.max_text_width = None; + // let tab_tooltip_text = + // item.tab_tooltip_text(cx).map(|text| text.into_owned()); + + // let mut tab_style = theme + // .workspace + // .tab_bar + // .tab_style(pane_active, tab_active) + // .clone(); + // let should_show_status = settings::get::(cx).git_status; + // if should_show_status && git_status != None { + // tab_style.label.text.color = match git_status.unwrap() { + // GitFileStatus::Added => tab_style.git.inserted, + // GitFileStatus::Modified => tab_style.git.modified, + // GitFileStatus::Conflict => tab_style.git.conflict, + // }; + // } + + // move |mouse_state, cx| { + // let hovered = mouse_state.hovered(); + + // enum Tab {} + // let mouse_event_handler = + // MouseEventHandler::new::(ix, cx, |_, cx| { + // Self::render_tab( + // &item, + // pane.clone(), + // ix == 0, + // detail, + // hovered, + // &tab_style, + // cx, + // ) + // }) + // .on_down(MouseButton::Left, move |_, this, cx| { + // this.activate_item(ix, true, true, cx); + // }) + // .on_click(MouseButton::Middle, { + // let item_id = item.id(); + // move |_, pane, cx| { + // pane.close_item_by_id(item_id, SaveIntent::Close, cx) + // .detach_and_log_err(cx); + // } + // }) + // .on_down( + // MouseButton::Right, + // move |event, pane, cx| { + // pane.deploy_tab_context_menu(event.position, item.id(), cx); + // }, + // ); + + // if let Some(tab_tooltip_text) = tab_tooltip_text { + // mouse_event_handler + // .with_tooltip::( + // ix, + // tab_tooltip_text, + // None, + // tooltip_theme, + // cx, + // ) + // .into_any() + // } else { + // mouse_event_handler.into_any() + // } + // } + // }); + + // if !pane_active || !tab_active { + // receiver = receiver.with_cursor_style(CursorStyle::PointingHand); + // } + + // receiver.as_draggable( + // DraggedItem { + // handle: item, + // pane: pane.clone(), + // }, + // { + // let theme = theme::current(cx).clone(); + + // let detail = detail.clone(); + // move |_, dragged_item: &DraggedItem, cx: &mut ViewContext| { + // let tab_style = &theme.workspace.tab_bar.dragged_tab; + // Self::render_dragged_tab( + // &dragged_item.handle, + // dragged_item.pane.clone(), + // false, + // detail, + // false, + // &tab_style, + // cx, + // ) + // } + // }, + // ) + // }) + // } + + // // Use the inactive tab style along with the current pane's active status to decide how to render + // // the filler + // let filler_index = self.items.len(); + // let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false); + // enum Filler {} + // row.add_child( + // dragged_item_receiver::(self, 0, filler_index, true, None, cx, |_, _| { + // Empty::new() + // .contained() + // .with_style(filler_style.container) + // .with_border(filler_style.container.border) + // }) + // .flex(1., true) + // .into_any_named("filler"), + // ); + + // row + // } + + // fn tab_details(&self, cx: &AppContext) -> Vec { + // let mut tab_details = (0..self.items.len()).map(|_| 0).collect::>(); + + // let mut tab_descriptions = HashMap::default(); + // let mut done = false; + // while !done { + // done = true; + + // // Store item indices by their tab description. + // for (ix, (item, detail)) in self.items.iter().zip(&tab_details).enumerate() { + // if let Some(description) = item.tab_description(*detail, cx) { + // if *detail == 0 + // || Some(&description) != item.tab_description(detail - 1, cx).as_ref() + // { + // tab_descriptions + // .entry(description) + // .or_insert(Vec::new()) + // .push(ix); + // } + // } + // } + + // // If two or more items have the same tab description, increase their level + // // of detail and try again. + // for (_, item_ixs) in tab_descriptions.drain() { + // if item_ixs.len() > 1 { + // done = false; + // for ix in item_ixs { + // tab_details[ix] += 1; + // } + // } + // } + // } + + // tab_details + // } + + // fn render_tab( + // item: &Box, + // pane: WeakViewHandle, + // first: bool, + // detail: Option, + // hovered: bool, + // tab_style: &theme::Tab, + // cx: &mut ViewContext, + // ) -> AnyElement { + // let title = item.tab_content(detail, &tab_style, cx); + // Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx) + // } + + // fn render_dragged_tab( + // item: &Box, + // pane: WeakViewHandle, + // first: bool, + // detail: Option, + // hovered: bool, + // tab_style: &theme::Tab, + // cx: &mut ViewContext, + // ) -> AnyElement { + // let title = item.dragged_tab_content(detail, &tab_style, cx); + // Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx) + // } + + // fn render_tab_with_title( + // title: AnyElement, + // item: &Box, + // pane: WeakViewHandle, + // first: bool, + // hovered: bool, + // tab_style: &theme::Tab, + // cx: &mut ViewContext, + // ) -> AnyElement { + // let mut container = tab_style.container.clone(); + // if first { + // container.border.left = false; + // } + + // let buffer_jewel_element = { + // let diameter = 7.0; + // let icon_color = if item.has_conflict(cx) { + // Some(tab_style.icon_conflict) + // } else if item.is_dirty(cx) { + // Some(tab_style.icon_dirty) + // } else { + // None + // }; + + // Canvas::new(move |bounds, _, _, cx| { + // if let Some(color) = icon_color { + // let square = RectF::new(bounds.origin(), vec2f(diameter, diameter)); + // cx.scene().push_quad(Quad { + // bounds: square, + // background: Some(color), + // border: Default::default(), + // corner_radii: (diameter / 2.).into(), + // }); + // } + // }) + // .constrained() + // .with_width(diameter) + // .with_height(diameter) + // .aligned() + // }; + + // let title_element = title.aligned().contained().with_style(ContainerStyle { + // margin: Margin { + // left: tab_style.spacing, + // right: tab_style.spacing, + // ..Default::default() + // }, + // ..Default::default() + // }); + + // let close_element = if hovered { + // let item_id = item.id(); + // enum TabCloseButton {} + // let icon = Svg::new("icons/x.svg"); + // MouseEventHandler::new::(item_id, cx, |mouse_state, _| { + // if mouse_state.hovered() { + // icon.with_color(tab_style.icon_close_active) + // } else { + // icon.with_color(tab_style.icon_close) + // } + // }) + // .with_padding(Padding::uniform(4.)) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_click(MouseButton::Left, { + // let pane = pane.clone(); + // move |_, _, cx| { + // let pane = pane.clone(); + // cx.window_context().defer(move |cx| { + // if let Some(pane) = pane.upgrade(cx) { + // pane.update(cx, |pane, cx| { + // pane.close_item_by_id(item_id, SaveIntent::Close, cx) + // .detach_and_log_err(cx); + // }); + // } + // }); + // } + // }) + // .into_any_named("close-tab-icon") + // .constrained() + // } else { + // Empty::new().constrained() + // } + // .with_width(tab_style.close_icon_width) + // .aligned(); + + // let close_right = settings::get::(cx).close_position.right(); + + // if close_right { + // Flex::row() + // .with_child(buffer_jewel_element) + // .with_child(title_element) + // .with_child(close_element) + // } else { + // Flex::row() + // .with_child(close_element) + // .with_child(title_element) + // .with_child(buffer_jewel_element) + // } + // .contained() + // .with_style(container) + // .constrained() + // .with_height(tab_style.height) + // .into_any() + // } + + // pub fn render_tab_bar_button< + // F1: 'static + Fn(&mut Pane, &mut EventContext), + // F2: 'static + Fn(&mut Pane, &mut EventContext), + // >( + // index: usize, + // icon: &'static str, + // is_active: bool, + // tooltip: Option<(&'static str, Option>)>, + // cx: &mut ViewContext, + // on_click: F1, + // on_down: F2, + // context_menu: Option>, + // ) -> AnyElement { + // enum TabBarButton {} + + // let mut button = MouseEventHandler::new::(index, cx, |mouse_state, cx| { + // let theme = &settings2::get::(cx).theme.workspace.tab_bar; + // let style = theme.pane_button.in_state(is_active).style_for(mouse_state); + // Svg::new(icon) + // .with_color(style.color) + // .constrained() + // .with_width(style.icon_width) + // .aligned() + // .constrained() + // .with_width(style.button_width) + // .with_height(style.button_width) + // }) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_down(MouseButton::Left, move |_, pane, cx| on_down(pane, cx)) + // .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx)) + // .into_any(); + // if let Some((tooltip, action)) = tooltip { + // let tooltip_style = settings::get::(cx).theme.tooltip.clone(); + // button = button + // .with_tooltip::(index, tooltip, action, tooltip_style, cx) + // .into_any(); + // } + + // Stack::new() + // .with_child(button) + // .with_children( + // context_menu.map(|menu| ChildView::new(&menu, cx).aligned().bottom().right()), + // ) + // .flex(1., false) + // .into_any_named("tab bar button") + // } + + // fn render_blank_pane(&self, theme: &Theme, _cx: &mut ViewContext) -> AnyElement { + // let background = theme.workspace.background; + // Empty::new() + // .contained() + // .with_background_color(background) + // .into_any() + // } + + // pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext) { + // self.zoomed = zoomed; + // cx.notify(); + // } + + // pub fn is_zoomed(&self) -> bool { + // self.zoomed + // } + // } + + // impl Entity for Pane { + // type Event = Event; + // } + + // impl View for Pane { + // fn ui_name() -> &'static str { + // "Pane" + // } + + // fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + // enum MouseNavigationHandler {} + + // MouseEventHandler::new::(0, cx, |_, cx| { + // let active_item_index = self.active_item_index; + + // if let Some(active_item) = self.active_item() { + // Flex::column() + // .with_child({ + // let theme = theme::current(cx).clone(); + + // let mut stack = Stack::new(); + + // enum TabBarEventHandler {} + // stack.add_child( + // MouseEventHandler::new::(0, cx, |_, _| { + // Empty::new() + // .contained() + // .with_style(theme.workspace.tab_bar.container) + // }) + // .on_down( + // MouseButton::Left, + // move |_, this, cx| { + // this.activate_item(active_item_index, true, true, cx); + // }, + // ), + // ); + // let tooltip_style = theme.tooltip.clone(); + // let tab_bar_theme = theme.workspace.tab_bar.clone(); + + // let nav_button_height = tab_bar_theme.height; + // let button_style = tab_bar_theme.nav_button; + // let border_for_nav_buttons = tab_bar_theme + // .tab_style(false, false) + // .container + // .border + // .clone(); + + // let mut tab_row = Flex::row() + // .with_child(nav_button( + // "icons/arrow_left.svg", + // button_style.clone(), + // nav_button_height, + // tooltip_style.clone(), + // self.can_navigate_backward(), + // { + // move |pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace + // .go_back(pane, cx) + // .detach_and_log_err(cx) + // }) + // }) + // } + // } + // }, + // super::GoBack, + // "Go Back", + // cx, + // )) + // .with_child( + // nav_button( + // "icons/arrow_right.svg", + // button_style.clone(), + // nav_button_height, + // tooltip_style, + // self.can_navigate_forward(), + // { + // move |pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace + // .go_forward(pane, cx) + // .detach_and_log_err(cx) + // }) + // }) + // } + // } + // }, + // super::GoForward, + // "Go Forward", + // cx, + // ) + // .contained() + // .with_border(border_for_nav_buttons), + // ) + // .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs")); + + // if self.has_focus { + // let render_tab_bar_buttons = self.render_tab_bar_buttons.clone(); + // tab_row.add_child( + // (render_tab_bar_buttons)(self, cx) + // .contained() + // .with_style(theme.workspace.tab_bar.pane_button_container) + // .flex(1., false) + // .into_any(), + // ) + // } + + // stack.add_child(tab_row); + // stack + // .constrained() + // .with_height(theme.workspace.tab_bar.height) + // .flex(1., false) + // .into_any_named("tab bar") + // }) + // .with_child({ + // enum PaneContentTabDropTarget {} + // dragged_item_receiver::( + // self, + // 0, + // self.active_item_index + 1, + // !self.can_split, + // if self.can_split { Some(100.) } else { None }, + // cx, + // { + // let toolbar = self.toolbar.clone(); + // let toolbar_hidden = toolbar.read(cx).hidden(); + // move |_, cx| { + // Flex::column() + // .with_children( + // (!toolbar_hidden) + // .then(|| ChildView::new(&toolbar, cx).expanded()), + // ) + // .with_child( + // ChildView::new(active_item.as_any(), cx).flex(1., true), + // ) + // } + // }, + // ) + // .flex(1., true) + // }) + // .with_child(ChildView::new(&self.tab_context_menu, cx)) + // .into_any() + // } else { + // enum EmptyPane {} + // let theme = theme::current(cx).clone(); + + // dragged_item_receiver::(self, 0, 0, false, None, cx, |_, cx| { + // self.render_blank_pane(&theme, cx) + // }) + // .on_down(MouseButton::Left, |_, _, cx| { + // cx.focus_parent(); + // }) + // .into_any() + // } + // }) + // .on_down( + // MouseButton::Navigate(NavigationDirection::Back), + // move |_, pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace.go_back(pane, cx).detach_and_log_err(cx) + // }) + // }) + // } + // }, + // ) + // .on_down(MouseButton::Navigate(NavigationDirection::Forward), { + // move |_, pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace.go_forward(pane, cx).detach_and_log_err(cx) + // }) + // }) + // } + // } + // }) + // .into_any_named("pane") + // } + + // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { + // if !self.has_focus { + // self.has_focus = true; + // cx.emit(Event::Focus); + // cx.notify(); + // } + + // self.toolbar.update(cx, |toolbar, cx| { + // toolbar.focus_changed(true, cx); + // }); + + // if let Some(active_item) = self.active_item() { + // if cx.is_self_focused() { + // // Pane was focused directly. We need to either focus a view inside the active item, + // // or focus the active item itself + // if let Some(weak_last_focused_view) = + // self.last_focused_view_by_item.get(&active_item.id()) + // { + // if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) { + // cx.focus(&last_focused_view); + // return; + // } else { + // self.last_focused_view_by_item.remove(&active_item.id()); + // } + // } + + // cx.focus(active_item.as_any()); + // } else if focused != self.tab_bar_context_menu.handle { + // self.last_focused_view_by_item + // .insert(active_item.id(), focused.downgrade()); + // } + // } + // } + + // fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + // self.has_focus = false; + // self.toolbar.update(cx, |toolbar, cx| { + // toolbar.focus_changed(false, cx); + // }); + // cx.notify(); + // } + + // fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) { + // Self::reset_to_default_keymap_context(keymap); + // } + // } + + // impl ItemNavHistory { + // pub fn push(&mut self, data: Option, cx: &mut WindowContext) { + // self.history.push(data, self.item.clone(), cx); + // } + + // pub fn pop_backward(&mut self, cx: &mut WindowContext) -> Option { + // self.history.pop(NavigationMode::GoingBack, cx) + // } + + // pub fn pop_forward(&mut self, cx: &mut WindowContext) -> Option { + // self.history.pop(NavigationMode::GoingForward, cx) + // } + // } + + // impl NavHistory { + // pub fn for_each_entry( + // &self, + // cx: &AppContext, + // mut f: impl FnMut(&NavigationEntry, (ProjectPath, Option)), + // ) { + // let borrowed_history = self.0.borrow(); + // borrowed_history + // .forward_stack + // .iter() + // .chain(borrowed_history.backward_stack.iter()) + // .chain(borrowed_history.closed_stack.iter()) + // .for_each(|entry| { + // if let Some(project_and_abs_path) = + // borrowed_history.paths_by_item.get(&entry.item.id()) + // { + // f(entry, project_and_abs_path.clone()); + // } else if let Some(item) = entry.item.upgrade(cx) { + // if let Some(path) = item.project_path(cx) { + // f(entry, (path, None)); + // } + // } + // }) + // } + + // pub fn set_mode(&mut self, mode: NavigationMode) { + // self.0.borrow_mut().mode = mode; + // } + + pub fn mode(&self) -> NavigationMode { + self.0.borrow().mode + } + + // pub fn disable(&mut self) { + // self.0.borrow_mut().mode = NavigationMode::Disabled; + // } + + // pub fn enable(&mut self) { + // self.0.borrow_mut().mode = NavigationMode::Normal; + // } + + // pub fn pop(&mut self, mode: NavigationMode, cx: &mut WindowContext) -> Option { + // let mut state = self.0.borrow_mut(); + // let entry = match mode { + // NavigationMode::Normal | NavigationMode::Disabled | NavigationMode::ClosingItem => { + // return None + // } + // NavigationMode::GoingBack => &mut state.backward_stack, + // NavigationMode::GoingForward => &mut state.forward_stack, + // NavigationMode::ReopeningClosedItem => &mut state.closed_stack, + // } + // .pop_back(); + // if entry.is_some() { + // state.did_update(cx); + // } + // entry + // } + + // pub fn push( + // &mut self, + // data: Option, + // item: Rc, + // cx: &mut WindowContext, + // ) { + // let state = &mut *self.0.borrow_mut(); + // match state.mode { + // NavigationMode::Disabled => {} + // NavigationMode::Normal | NavigationMode::ReopeningClosedItem => { + // if state.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + // state.backward_stack.pop_front(); + // } + // state.backward_stack.push_back(NavigationEntry { + // item, + // data: data.map(|data| Box::new(data) as Box), + // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + // }); + // state.forward_stack.clear(); + // } + // NavigationMode::GoingBack => { + // if state.forward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + // state.forward_stack.pop_front(); + // } + // state.forward_stack.push_back(NavigationEntry { + // item, + // data: data.map(|data| Box::new(data) as Box), + // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + // }); + // } + // NavigationMode::GoingForward => { + // if state.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + // state.backward_stack.pop_front(); + // } + // state.backward_stack.push_back(NavigationEntry { + // item, + // data: data.map(|data| Box::new(data) as Box), + // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + // }); + // } + // NavigationMode::ClosingItem => { + // if state.closed_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + // state.closed_stack.pop_front(); + // } + // state.closed_stack.push_back(NavigationEntry { + // item, + // data: data.map(|data| Box::new(data) as Box), + // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + // }); + // } + // } + // state.did_update(cx); + // } + + // pub fn remove_item(&mut self, item_id: usize) { + // let mut state = self.0.borrow_mut(); + // state.paths_by_item.remove(&item_id); + // state + // .backward_stack + // .retain(|entry| entry.item.id() != item_id); + // state + // .forward_stack + // .retain(|entry| entry.item.id() != item_id); + // state + // .closed_stack + // .retain(|entry| entry.item.id() != item_id); + // } + + // pub fn path_for_item(&self, item_id: usize) -> Option<(ProjectPath, Option)> { + // self.0.borrow().paths_by_item.get(&item_id).cloned() + // } +} + +// impl NavHistoryState { +// pub fn did_update(&self, cx: &mut WindowContext) { +// if let Some(pane) = self.pane.upgrade(cx) { +// cx.defer(move |cx| { +// pane.update(cx, |pane, cx| pane.history_updated(cx)); +// }); +// } +// } +// } + +// pub struct PaneBackdrop { +// child_view: usize, +// child: AnyElement, +// } + +// impl PaneBackdrop { +// pub fn new(pane_item_view: usize, child: AnyElement) -> Self { +// PaneBackdrop { +// child, +// child_view: pane_item_view, +// } +// } +// } + +// impl Element for PaneBackdrop { +// type LayoutState = (); + +// type PaintState = (); + +// fn layout( +// &mut self, +// constraint: gpui::SizeConstraint, +// view: &mut V, +// cx: &mut ViewContext, +// ) -> (Vector2F, Self::LayoutState) { +// let size = self.child.layout(constraint, view, cx); +// (size, ()) +// } + +// fn paint( +// &mut self, +// bounds: RectF, +// visible_bounds: RectF, +// _: &mut Self::LayoutState, +// view: &mut V, +// cx: &mut ViewContext, +// ) -> Self::PaintState { +// let background = theme::current(cx).editor.background; + +// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + +// cx.scene().push_quad(gpui::Quad { +// bounds: RectF::new(bounds.origin(), bounds.size()), +// background: Some(background), +// ..Default::default() +// }); + +// let child_view_id = self.child_view; +// cx.scene().push_mouse_region( +// MouseRegion::new::(child_view_id, 0, visible_bounds).on_down( +// gpui::platform::MouseButton::Left, +// move |_, _: &mut V, cx| { +// let window = cx.window(); +// cx.app_context().focus(window, Some(child_view_id)) +// }, +// ), +// ); + +// cx.scene().push_layer(Some(bounds)); +// self.child.paint(bounds.origin(), visible_bounds, view, cx); +// cx.scene().pop_layer(); +// } + +// fn rect_for_text_range( +// &self, +// range_utf16: std::ops::Range, +// _bounds: RectF, +// _visible_bounds: RectF, +// _layout: &Self::LayoutState, +// _paint: &Self::PaintState, +// view: &V, +// cx: &gpui::ViewContext, +// ) -> Option { +// self.child.rect_for_text_range(range_utf16, view, cx) +// } + +// fn debug( +// &self, +// _bounds: RectF, +// _layout: &Self::LayoutState, +// _paint: &Self::PaintState, +// view: &V, +// cx: &gpui::ViewContext, +// ) -> serde_json::Value { +// gpui::json::json!({ +// "type": "Pane Back Drop", +// "view": self.child_view, +// "child": self.child.debug(view, cx), +// }) +// } +// } + +// fn dirty_message_for(buffer_path: Option) -> String { +// let path = buffer_path +// .as_ref() +// .and_then(|p| p.path.to_str()) +// .unwrap_or(&"This buffer"); +// let path = truncate_and_remove_front(path, 80); +// format!("{path} contains unsaved edits. Do you want to save it?") +// } + +// todo!("uncomment tests") +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::item::test::{TestItem, TestProjectItem}; +// use gpui::TestAppContext; +// use project::FakeFs; +// use settings::SettingsStore; + +// #[gpui::test] +// async fn test_remove_active_empty(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// pane.update(cx, |pane, cx| { +// assert!(pane +// .close_active_item(&CloseActiveItem { save_intent: None }, cx) +// .is_none()) +// }); +// } + +// #[gpui::test] +// async fn test_add_item_with_new_item(cx: &mut TestAppContext) { +// cx.foreground().forbid_parking(); +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// // 1. Add with a destination index +// // a. Add before the active item +// set_labeled_items(&pane, ["A", "B*", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item( +// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), +// false, +// false, +// Some(0), +// cx, +// ); +// }); +// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx); + +// // b. Add after the active item +// set_labeled_items(&pane, ["A", "B*", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item( +// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), +// false, +// false, +// Some(2), +// cx, +// ); +// }); +// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx); + +// // c. Add at the end of the item list (including off the length) +// set_labeled_items(&pane, ["A", "B*", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item( +// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), +// false, +// false, +// Some(5), +// cx, +// ); +// }); +// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + +// // 2. Add without a destination index +// // a. Add with active item at the start of the item list +// set_labeled_items(&pane, ["A*", "B", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item( +// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), +// false, +// false, +// None, +// cx, +// ); +// }); +// set_labeled_items(&pane, ["A", "D*", "B", "C"], cx); + +// // b. Add with active item at the end of the item list +// set_labeled_items(&pane, ["A", "B", "C*"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item( +// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), +// false, +// false, +// None, +// cx, +// ); +// }); +// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); +// } + +// #[gpui::test] +// async fn test_add_item_with_existing_item(cx: &mut TestAppContext) { +// cx.foreground().forbid_parking(); +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// // 1. Add with a destination index +// // 1a. Add before the active item +// let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(d, false, false, Some(0), cx); +// }); +// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx); + +// // 1b. Add after the active item +// let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(d, false, false, Some(2), cx); +// }); +// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx); + +// // 1c. Add at the end of the item list (including off the length) +// let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(a, false, false, Some(5), cx); +// }); +// assert_item_labels(&pane, ["B", "C", "D", "A*"], cx); + +// // 1d. Add same item to active index +// let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(b, false, false, Some(1), cx); +// }); +// assert_item_labels(&pane, ["A", "B*", "C"], cx); + +// // 1e. Add item to index after same item in last position +// let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(c, false, false, Some(2), cx); +// }); +// assert_item_labels(&pane, ["A", "B", "C*"], cx); + +// // 2. Add without a destination index +// // 2a. Add with active item at the start of the item list +// let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(d, false, false, None, cx); +// }); +// assert_item_labels(&pane, ["A", "D*", "B", "C"], cx); + +// // 2b. Add with active item at the end of the item list +// let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(a, false, false, None, cx); +// }); +// assert_item_labels(&pane, ["B", "C", "D", "A*"], cx); + +// // 2c. Add active item to active item at end of list +// let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(c, false, false, None, cx); +// }); +// assert_item_labels(&pane, ["A", "B", "C*"], cx); + +// // 2d. Add active item to active item at start of list +// let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx); +// pane.update(cx, |pane, cx| { +// pane.add_item(a, false, false, None, cx); +// }); +// assert_item_labels(&pane, ["A*", "B", "C"], cx); +// } + +// #[gpui::test] +// async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) { +// cx.foreground().forbid_parking(); +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// // singleton view +// pane.update(cx, |pane, cx| { +// let item = TestItem::new() +// .with_singleton(true) +// .with_label("buffer 1") +// .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]); + +// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); +// }); +// assert_item_labels(&pane, ["buffer 1*"], cx); + +// // new singleton view with the same project entry +// pane.update(cx, |pane, cx| { +// let item = TestItem::new() +// .with_singleton(true) +// .with_label("buffer 1") +// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]); + +// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); +// }); +// assert_item_labels(&pane, ["buffer 1*"], cx); + +// // new singleton view with different project entry +// pane.update(cx, |pane, cx| { +// let item = TestItem::new() +// .with_singleton(true) +// .with_label("buffer 2") +// .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]); +// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); +// }); +// assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx); + +// // new multibuffer view with the same project entry +// pane.update(cx, |pane, cx| { +// let item = TestItem::new() +// .with_singleton(false) +// .with_label("multibuffer 1") +// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]); + +// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); +// }); +// assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx); + +// // another multibuffer view with the same project entry +// pane.update(cx, |pane, cx| { +// let item = TestItem::new() +// .with_singleton(false) +// .with_label("multibuffer 1b") +// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]); + +// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); +// }); +// assert_item_labels( +// &pane, +// ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"], +// cx, +// ); +// } + +// #[gpui::test] +// async fn test_remove_item_ordering(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// add_labeled_item(&pane, "A", false, cx); +// add_labeled_item(&pane, "B", false, cx); +// add_labeled_item(&pane, "C", false, cx); +// add_labeled_item(&pane, "D", false, cx); +// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + +// pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx)); +// add_labeled_item(&pane, "1", false, cx); +// assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["A", "B*", "C", "D"], cx); + +// pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx)); +// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["A", "B*", "C"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["A", "C*"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["A*"], cx); +// } + +// #[gpui::test] +// async fn test_close_inactive_items(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_inactive_items(&CloseInactiveItems, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["C*"], cx); +// } + +// #[gpui::test] +// async fn test_close_clean_items(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// add_labeled_item(&pane, "A", true, cx); +// add_labeled_item(&pane, "B", false, cx); +// add_labeled_item(&pane, "C", true, cx); +// add_labeled_item(&pane, "D", false, cx); +// add_labeled_item(&pane, "E", false, cx); +// assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx); + +// pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx)) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["A^", "C*^"], cx); +// } + +// #[gpui::test] +// async fn test_close_items_to_the_left(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_items_to_the_left(&CloseItemsToTheLeft, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["C*", "D", "E"], cx); +// } + +// #[gpui::test] +// async fn test_close_items_to_the_right(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_items_to_the_right(&CloseItemsToTheRight, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, ["A", "B", "C*"], cx); +// } + +// #[gpui::test] +// async fn test_close_all_items(cx: &mut TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// add_labeled_item(&pane, "A", false, cx); +// add_labeled_item(&pane, "B", false, cx); +// add_labeled_item(&pane, "C", false, cx); +// assert_item_labels(&pane, ["A", "B", "C*"], cx); + +// pane.update(cx, |pane, cx| { +// pane.close_all_items(&CloseAllItems { save_intent: None }, cx) +// }) +// .unwrap() +// .await +// .unwrap(); +// assert_item_labels(&pane, [], cx); + +// add_labeled_item(&pane, "A", true, cx); +// add_labeled_item(&pane, "B", true, cx); +// add_labeled_item(&pane, "C", true, cx); +// assert_item_labels(&pane, ["A^", "B^", "C*^"], cx); + +// let save = pane +// .update(cx, |pane, cx| { +// pane.close_all_items(&CloseAllItems { save_intent: None }, cx) +// }) +// .unwrap(); + +// cx.foreground().run_until_parked(); +// window.simulate_prompt_answer(2, cx); +// save.await.unwrap(); +// assert_item_labels(&pane, [], cx); +// } + +// fn init_test(cx: &mut TestAppContext) { +// cx.update(|cx| { +// cx.set_global(SettingsStore::test(cx)); +// theme::init((), cx); +// crate::init_settings(cx); +// Project::init_settings(cx); +// }); +// } + +// fn add_labeled_item( +// pane: &ViewHandle, +// label: &str, +// is_dirty: bool, +// cx: &mut TestAppContext, +// ) -> Box> { +// pane.update(cx, |pane, cx| { +// let labeled_item = +// Box::new(cx.add_view(|_| TestItem::new().with_label(label).with_dirty(is_dirty))); +// pane.add_item(labeled_item.clone(), false, false, None, cx); +// labeled_item +// }) +// } + +// fn set_labeled_items( +// pane: &ViewHandle, +// labels: [&str; COUNT], +// cx: &mut TestAppContext, +// ) -> [Box>; COUNT] { +// pane.update(cx, |pane, cx| { +// pane.items.clear(); +// let mut active_item_index = 0; + +// let mut index = 0; +// let items = labels.map(|mut label| { +// if label.ends_with("*") { +// label = label.trim_end_matches("*"); +// active_item_index = index; +// } + +// let labeled_item = Box::new(cx.add_view(|_| TestItem::new().with_label(label))); +// pane.add_item(labeled_item.clone(), false, false, None, cx); +// index += 1; +// labeled_item +// }); + +// pane.activate_item(active_item_index, false, false, cx); + +// items +// }) +// } + +// // Assert the item label, with the active item label suffixed with a '*' +// fn assert_item_labels( +// pane: &ViewHandle, +// expected_states: [&str; COUNT], +// cx: &mut TestAppContext, +// ) { +// pane.read_with(cx, |pane, cx| { +// let actual_states = pane +// .items +// .iter() +// .enumerate() +// .map(|(ix, item)| { +// let mut state = item +// .as_any() +// .downcast_ref::() +// .unwrap() +// .read(cx) +// .label +// .clone(); +// if ix == pane.active_item_index { +// state.push('*'); +// } +// if item.is_dirty(cx) { +// state.push('^'); +// } +// state +// }) +// .collect::>(); + +// assert_eq!( +// actual_states, expected_states, +// "pane items do not match expectation" +// ); +// }) +// } +// } diff --git a/crates/workspace2/src/pane_group.rs b/crates/workspace2/src/pane_group.rs new file mode 100644 index 0000000000..f226f7fc43 --- /dev/null +++ b/crates/workspace2/src/pane_group.rs @@ -0,0 +1,993 @@ +use crate::{AppState, FollowerState, Pane, Workspace}; +use anyhow::{anyhow, Result}; +use call2::ActiveCall; +use collections::HashMap; +use gpui2::{size, AnyElement, AnyView, Bounds, Handle, Pixels, Point, View, ViewContext}; +use project2::Project; +use serde::Deserialize; +use std::{cell::RefCell, rc::Rc, sync::Arc}; +use theme2::Theme; + +const HANDLE_HITBOX_SIZE: f32 = 4.0; +const HORIZONTAL_MIN_SIZE: f32 = 80.; +const VERTICAL_MIN_SIZE: f32 = 100.; + +pub enum Axis { + Vertical, + Horizontal, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct PaneGroup { + pub(crate) root: Member, +} + +impl PaneGroup { + pub(crate) fn with_root(root: Member) -> Self { + Self { root } + } + + pub fn new(pane: View) -> Self { + Self { + root: Member::Pane(pane), + } + } + + pub fn split( + &mut self, + old_pane: &View, + new_pane: &View, + direction: SplitDirection, + ) -> Result<()> { + match &mut self.root { + Member::Pane(pane) => { + if pane == old_pane { + self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction); + Ok(()) + } else { + Err(anyhow!("Pane not found")) + } + } + Member::Axis(axis) => axis.split(old_pane, new_pane, direction), + } + } + + pub fn bounding_box_for_pane(&self, pane: &View) -> Option> { + match &self.root { + Member::Pane(_) => None, + Member::Axis(axis) => axis.bounding_box_for_pane(pane), + } + } + + pub fn pane_at_pixel_position(&self, coordinate: Point) -> Option<&View> { + match &self.root { + Member::Pane(pane) => Some(pane), + Member::Axis(axis) => axis.pane_at_pixel_position(coordinate), + } + } + + /// Returns: + /// - Ok(true) if it found and removed a pane + /// - Ok(false) if it found but did not remove the pane + /// - Err(_) if it did not find the pane + pub fn remove(&mut self, pane: &View) -> Result { + match &mut self.root { + Member::Pane(_) => Ok(false), + Member::Axis(axis) => { + if let Some(last_pane) = axis.remove(pane)? { + self.root = last_pane; + } + Ok(true) + } + } + } + + pub fn swap(&mut self, from: &View, to: &View) { + match &mut self.root { + Member::Pane(_) => {} + Member::Axis(axis) => axis.swap(from, to), + }; + } + + pub(crate) fn render( + &self, + project: &Handle, + theme: &Theme, + follower_states: &HashMap, FollowerState>, + active_call: Option<&Handle>, + active_pane: &View, + zoomed: Option<&AnyView>, + app_state: &Arc, + cx: &mut ViewContext, + ) -> AnyElement { + self.root.render( + project, + 0, + theme, + follower_states, + active_call, + active_pane, + zoomed, + app_state, + cx, + ) + } + + pub(crate) fn panes(&self) -> Vec<&View> { + let mut panes = Vec::new(); + self.root.collect_panes(&mut panes); + panes + } +} + +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Member { + Axis(PaneAxis), + Pane(View), +} + +impl Member { + fn new_axis(old_pane: View, new_pane: View, direction: SplitDirection) -> Self { + use Axis::*; + use SplitDirection::*; + + let axis = match direction { + Up | Down => Vertical, + Left | Right => Horizontal, + }; + + let members = match direction { + Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)], + Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)], + }; + + Member::Axis(PaneAxis::new(axis, members)) + } + + fn contains(&self, needle: &View) -> bool { + match self { + Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)), + Member::Pane(pane) => pane == needle, + } + } + + pub fn render( + &self, + project: &Handle, + basis: usize, + theme: &Theme, + follower_states: &HashMap, FollowerState>, + active_call: Option<&Handle>, + active_pane: &View, + zoomed: Option<&AnyView>, + app_state: &Arc, + cx: &mut ViewContext, + ) -> AnyElement { + todo!() + + // enum FollowIntoExternalProject {} + + // match self { + // Member::Pane(pane) => { + // let pane_element = if Some(&**pane) == zoomed { + // Empty::new().into_any() + // } else { + // ChildView::new(pane, cx).into_any() + // }; + + // let leader = follower_states.get(pane).and_then(|state| { + // let room = active_call?.read(cx).room()?.read(cx); + // room.remote_participant_for_peer_id(state.leader_id) + // }); + + // let mut leader_border = Border::default(); + // let mut leader_status_box = None; + // if let Some(leader) = &leader { + // let leader_color = theme + // .editor + // .selection_style_for_room_participant(leader.participant_index.0) + // .cursor; + // leader_border = Border::all(theme.workspace.leader_border_width, leader_color); + // leader_border + // .color + // .fade_out(1. - theme.workspace.leader_border_opacity); + // leader_border.overlay = true; + + // leader_status_box = match leader.location { + // ParticipantLocation::SharedProject { + // project_id: leader_project_id, + // } => { + // if Some(leader_project_id) == project.read(cx).remote_id() { + // None + // } else { + // let leader_user = leader.user.clone(); + // let leader_user_id = leader.user.id; + // Some( + // MouseEventHandler::new::( + // pane.id(), + // cx, + // |_, _| { + // Label::new( + // format!( + // "Follow {} to their active project", + // leader_user.github_login, + // ), + // theme + // .workspace + // .external_location_message + // .text + // .clone(), + // ) + // .contained() + // .with_style( + // theme.workspace.external_location_message.container, + // ) + // }, + // ) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_click(MouseButton::Left, move |_, this, cx| { + // crate::join_remote_project( + // leader_project_id, + // leader_user_id, + // this.app_state().clone(), + // cx, + // ) + // .detach_and_log_err(cx); + // }) + // .aligned() + // .bottom() + // .right() + // .into_any(), + // ) + // } + // } + // ParticipantLocation::UnsharedProject => Some( + // Label::new( + // format!( + // "{} is viewing an unshared Zed project", + // leader.user.github_login + // ), + // theme.workspace.external_location_message.text.clone(), + // ) + // .contained() + // .with_style(theme.workspace.external_location_message.container) + // .aligned() + // .bottom() + // .right() + // .into_any(), + // ), + // ParticipantLocation::External => Some( + // Label::new( + // format!( + // "{} is viewing a window outside of Zed", + // leader.user.github_login + // ), + // theme.workspace.external_location_message.text.clone(), + // ) + // .contained() + // .with_style(theme.workspace.external_location_message.container) + // .aligned() + // .bottom() + // .right() + // .into_any(), + // ), + // }; + // } + + // Stack::new() + // .with_child(pane_element.contained().with_border(leader_border)) + // .with_children(leader_status_box) + // .into_any() + // } + // Member::Axis(axis) => axis.render( + // project, + // basis + 1, + // theme, + // follower_states, + // active_call, + // active_pane, + // zoomed, + // app_state, + // cx, + // ), + // } + } + + fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a View>) { + match self { + Member::Axis(axis) => { + for member in &axis.members { + member.collect_panes(panes); + } + } + Member::Pane(pane) => panes.push(pane), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct PaneAxis { + pub axis: Axis, + pub members: Vec, + pub flexes: Rc>>, + pub bounding_boxes: Rc>>>>, +} + +impl PaneAxis { + pub fn new(axis: Axis, members: Vec) -> Self { + let flexes = Rc::new(RefCell::new(vec![1.; members.len()])); + let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()])); + Self { + axis, + members, + flexes, + bounding_boxes, + } + } + + pub fn load(axis: Axis, members: Vec, flexes: Option>) -> Self { + let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]); + debug_assert!(members.len() == flexes.len()); + + let flexes = Rc::new(RefCell::new(flexes)); + let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()])); + Self { + axis, + members, + flexes, + bounding_boxes, + } + } + + fn split( + &mut self, + old_pane: &View, + new_pane: &View, + direction: SplitDirection, + ) -> Result<()> { + for (mut idx, member) in self.members.iter_mut().enumerate() { + match member { + Member::Axis(axis) => { + if axis.split(old_pane, new_pane, direction).is_ok() { + return Ok(()); + } + } + Member::Pane(pane) => { + if pane == old_pane { + if direction.axis() == self.axis { + if direction.increasing() { + idx += 1; + } + + self.members.insert(idx, Member::Pane(new_pane.clone())); + *self.flexes.borrow_mut() = vec![1.; self.members.len()]; + } else { + *member = + Member::new_axis(old_pane.clone(), new_pane.clone(), direction); + } + return Ok(()); + } + } + } + } + Err(anyhow!("Pane not found")) + } + + fn remove(&mut self, pane_to_remove: &View) -> Result> { + let mut found_pane = false; + let mut remove_member = None; + for (idx, member) in self.members.iter_mut().enumerate() { + match member { + Member::Axis(axis) => { + if let Ok(last_pane) = axis.remove(pane_to_remove) { + if let Some(last_pane) = last_pane { + *member = last_pane; + } + found_pane = true; + break; + } + } + Member::Pane(pane) => { + if pane == pane_to_remove { + found_pane = true; + remove_member = Some(idx); + break; + } + } + } + } + + if found_pane { + if let Some(idx) = remove_member { + self.members.remove(idx); + *self.flexes.borrow_mut() = vec![1.; self.members.len()]; + } + + if self.members.len() == 1 { + let result = self.members.pop(); + *self.flexes.borrow_mut() = vec![1.; self.members.len()]; + Ok(result) + } else { + Ok(None) + } + } else { + Err(anyhow!("Pane not found")) + } + } + + fn swap(&mut self, from: &View, to: &View) { + for member in self.members.iter_mut() { + match member { + Member::Axis(axis) => axis.swap(from, to), + Member::Pane(pane) => { + if pane == from { + *member = Member::Pane(to.clone()); + } else if pane == to { + *member = Member::Pane(from.clone()) + } + } + } + } + } + + fn bounding_box_for_pane(&self, pane: &View) -> Option> { + debug_assert!(self.members.len() == self.bounding_boxes.borrow().len()); + + for (idx, member) in self.members.iter().enumerate() { + match member { + Member::Pane(found) => { + if pane == found { + return self.bounding_boxes.borrow()[idx]; + } + } + Member::Axis(axis) => { + if let Some(rect) = axis.bounding_box_for_pane(pane) { + return Some(rect); + } + } + } + } + None + } + + fn pane_at_pixel_position(&self, coordinate: Point) -> Option<&View> { + debug_assert!(self.members.len() == self.bounding_boxes.borrow().len()); + + let bounding_boxes = self.bounding_boxes.borrow(); + + for (idx, member) in self.members.iter().enumerate() { + if let Some(coordinates) = bounding_boxes[idx] { + if coordinates.contains_point(&coordinate) { + return match member { + Member::Pane(found) => Some(found), + Member::Axis(axis) => axis.pane_at_pixel_position(coordinate), + }; + } + } + } + None + } + + fn render( + &self, + project: &Handle, + basis: usize, + theme: &Theme, + follower_states: &HashMap, FollowerState>, + active_call: Option<&Handle>, + active_pane: &View, + zoomed: Option<&AnyView>, + app_state: &Arc, + cx: &mut ViewContext, + ) -> AnyElement { + debug_assert!(self.members.len() == self.flexes.borrow().len()); + + todo!() + // let mut pane_axis = PaneAxisElement::new( + // self.axis, + // basis, + // self.flexes.clone(), + // self.bounding_boxes.clone(), + // ); + // let mut active_pane_ix = None; + + // let mut members = self.members.iter().enumerate().peekable(); + // while let Some((ix, member)) = members.next() { + // let last = members.peek().is_none(); + + // if member.contains(active_pane) { + // active_pane_ix = Some(ix); + // } + + // let mut member = member.render( + // project, + // (basis + ix) * 10, + // theme, + // follower_states, + // active_call, + // active_pane, + // zoomed, + // app_state, + // cx, + // ); + + // if !last { + // let mut border = theme.workspace.pane_divider; + // border.left = false; + // border.right = false; + // border.top = false; + // border.bottom = false; + + // match self.axis { + // Axis::Vertical => border.bottom = true, + // Axis::Horizontal => border.right = true, + // } + + // member = member.contained().with_border(border).into_any(); + // } + + // pane_axis = pane_axis.with_child(member.into_any()); + // } + // pane_axis.set_active_pane(active_pane_ix); + // pane_axis.into_any() + } +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +pub enum SplitDirection { + Up, + Down, + Left, + Right, +} + +impl SplitDirection { + pub fn all() -> [Self; 4] { + [Self::Up, Self::Down, Self::Left, Self::Right] + } + + pub fn edge(&self, rect: Bounds) -> f32 { + match self { + Self::Up => rect.min_y(), + Self::Down => rect.max_y(), + Self::Left => rect.min_x(), + Self::Right => rect.max_x(), + } + } + + pub fn along_edge(&self, bounds: Bounds, length: Pixels) -> Bounds { + match self { + Self::Up => Bounds { + origin: bounds.origin(), + size: size(bounds.width(), length), + }, + Self::Down => Bounds { + origin: size(bounds.min_x(), bounds.max_y() - length), + size: size(bounds.width(), length), + }, + Self::Left => Bounds { + origin: bounds.origin(), + size: size(length, bounds.height()), + }, + Self::Right => Bounds { + origin: size(bounds.max_x() - length, bounds.min_y()), + size: size(length, bounds.height()), + }, + } + } + + pub fn axis(&self) -> Axis { + match self { + Self::Up | Self::Down => Axis::Vertical, + Self::Left | Self::Right => Axis::Horizontal, + } + } + + pub fn increasing(&self) -> bool { + match self { + Self::Left | Self::Up => false, + Self::Down | Self::Right => true, + } + } +} + +// mod element { +// // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc}; + +// // use gpui::{ +// // geometry::{ +// // rect::Bounds, +// // vector::{vec2f, Vector2F}, +// // }, +// // json::{self, ToJson}, +// // platform::{CursorStyle, MouseButton}, +// // scene::MouseDrag, +// // AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, BoundsExt, +// // SizeConstraint, Vector2FExt, ViewContext, +// // }; + +// use crate::{ +// pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE}, +// Workspace, WorkspaceSettings, +// }; + +// pub struct PaneAxisElement { +// axis: Axis, +// basis: usize, +// active_pane_ix: Option, +// flexes: Rc>>, +// children: Vec>, +// bounding_boxes: Rc>>>>, +// } + +// impl PaneAxisElement { +// pub fn new( +// axis: Axis, +// basis: usize, +// flexes: Rc>>, +// bounding_boxes: Rc>>>>, +// ) -> Self { +// Self { +// axis, +// basis, +// flexes, +// bounding_boxes, +// active_pane_ix: None, +// children: Default::default(), +// } +// } + +// pub fn set_active_pane(&mut self, active_pane_ix: Option) { +// self.active_pane_ix = active_pane_ix; +// } + +// fn layout_children( +// &mut self, +// active_pane_magnification: f32, +// constraint: SizeConstraint, +// remaining_space: &mut f32, +// remaining_flex: &mut f32, +// cross_axis_max: &mut f32, +// view: &mut Workspace, +// cx: &mut ViewContext, +// ) { +// let flexes = self.flexes.borrow(); +// let cross_axis = self.axis.invert(); +// for (ix, child) in self.children.iter_mut().enumerate() { +// let flex = if active_pane_magnification != 1. { +// if let Some(active_pane_ix) = self.active_pane_ix { +// if ix == active_pane_ix { +// active_pane_magnification +// } else { +// 1. +// } +// } else { +// 1. +// } +// } else { +// flexes[ix] +// }; + +// let child_size = if *remaining_flex == 0.0 { +// *remaining_space +// } else { +// let space_per_flex = *remaining_space / *remaining_flex; +// space_per_flex * flex +// }; + +// let child_constraint = match self.axis { +// Axis::Horizontal => SizeConstraint::new( +// vec2f(child_size, constraint.min.y()), +// vec2f(child_size, constraint.max.y()), +// ), +// Axis::Vertical => SizeConstraint::new( +// vec2f(constraint.min.x(), child_size), +// vec2f(constraint.max.x(), child_size), +// ), +// }; +// let child_size = child.layout(child_constraint, view, cx); +// *remaining_space -= child_size.along(self.axis); +// *remaining_flex -= flex; +// *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis)); +// } +// } + +// fn handle_resize( +// flexes: Rc>>, +// axis: Axis, +// preceding_ix: usize, +// child_start: Vector2F, +// drag_bounds: Bounds, +// ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext) { +// let size = move |ix, flexes: &[f32]| { +// drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32) +// }; + +// move |drag, workspace: &mut Workspace, cx| { +// if drag.end { +// // TODO: Clear cascading resize state +// return; +// } +// let min_size = match axis { +// Axis::Horizontal => HORIZONTAL_MIN_SIZE, +// Axis::Vertical => VERTICAL_MIN_SIZE, +// }; +// let mut flexes = flexes.borrow_mut(); + +// // Don't allow resizing to less than the minimum size, if elements are already too small +// if min_size - 1. > size(preceding_ix, flexes.as_slice()) { +// return; +// } + +// let mut proposed_current_pixel_change = (drag.position - child_start).along(axis) +// - size(preceding_ix, flexes.as_slice()); + +// let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| { +// let flex_change = pixel_dx / drag_bounds.length_along(axis); +// let current_target_flex = flexes[target_ix] + flex_change; +// let next_target_flex = +// flexes[(target_ix as isize + next) as usize] - flex_change; +// (current_target_flex, next_target_flex) +// }; + +// let mut successors = from_fn({ +// let forward = proposed_current_pixel_change > 0.; +// let mut ix_offset = 0; +// let len = flexes.len(); +// move || { +// let result = if forward { +// (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset) +// } else { +// (preceding_ix as isize - ix_offset as isize >= 0) +// .then(|| preceding_ix - ix_offset) +// }; + +// ix_offset += 1; + +// result +// } +// }); + +// while proposed_current_pixel_change.abs() > 0. { +// let Some(current_ix) = successors.next() else { +// break; +// }; + +// let next_target_size = f32::max( +// size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change, +// min_size, +// ); + +// let current_target_size = f32::max( +// size(current_ix, flexes.as_slice()) +// + size(current_ix + 1, flexes.as_slice()) +// - next_target_size, +// min_size, +// ); + +// let current_pixel_change = +// current_target_size - size(current_ix, flexes.as_slice()); + +// let (current_target_flex, next_target_flex) = +// flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice()); + +// flexes[current_ix] = current_target_flex; +// flexes[current_ix + 1] = next_target_flex; + +// proposed_current_pixel_change -= current_pixel_change; +// } + +// workspace.schedule_serialize(cx); +// cx.notify(); +// } +// } +// } + +// impl Extend> for PaneAxisElement { +// fn extend>>(&mut self, children: T) { +// self.children.extend(children); +// } +// } + +// impl Element for PaneAxisElement { +// type LayoutState = f32; +// type PaintState = (); + +// fn layout( +// &mut self, +// constraint: SizeConstraint, +// view: &mut Workspace, +// cx: &mut ViewContext, +// ) -> (Vector2F, Self::LayoutState) { +// debug_assert!(self.children.len() == self.flexes.borrow().len()); + +// let active_pane_magnification = +// settings::get::(cx).active_pane_magnification; + +// let mut remaining_flex = 0.; + +// if active_pane_magnification != 1. { +// let active_pane_flex = self +// .active_pane_ix +// .map(|_| active_pane_magnification) +// .unwrap_or(1.); +// remaining_flex += self.children.len() as f32 - 1. + active_pane_flex; +// } else { +// for flex in self.flexes.borrow().iter() { +// remaining_flex += flex; +// } +// } + +// let mut cross_axis_max: f32 = 0.0; +// let mut remaining_space = constraint.max_along(self.axis); + +// if remaining_space.is_infinite() { +// panic!("flex contains flexible children but has an infinite constraint along the flex axis"); +// } + +// self.layout_children( +// active_pane_magnification, +// constraint, +// &mut remaining_space, +// &mut remaining_flex, +// &mut cross_axis_max, +// view, +// cx, +// ); + +// let mut size = match self.axis { +// Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max), +// Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space), +// }; + +// if constraint.min.x().is_finite() { +// size.set_x(size.x().max(constraint.min.x())); +// } +// if constraint.min.y().is_finite() { +// size.set_y(size.y().max(constraint.min.y())); +// } + +// if size.x() > constraint.max.x() { +// size.set_x(constraint.max.x()); +// } +// if size.y() > constraint.max.y() { +// size.set_y(constraint.max.y()); +// } + +// (size, remaining_space) +// } + +// fn paint( +// &mut self, +// bounds: Bounds, +// visible_bounds: Bounds, +// remaining_space: &mut Self::LayoutState, +// view: &mut Workspace, +// cx: &mut ViewContext, +// ) -> Self::PaintState { +// let can_resize = settings::get::(cx).active_pane_magnification == 1.; +// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + +// let overflowing = *remaining_space < 0.; +// if overflowing { +// cx.scene().push_layer(Some(visible_bounds)); +// } + +// let mut child_origin = bounds.origin(); + +// let mut bounding_boxes = self.bounding_boxes.borrow_mut(); +// bounding_boxes.clear(); + +// let mut children_iter = self.children.iter_mut().enumerate().peekable(); +// while let Some((ix, child)) = children_iter.next() { +// let child_start = child_origin.clone(); +// child.paint(child_origin, visible_bounds, view, cx); + +// bounding_boxes.push(Some(Bounds::new(child_origin, child.size()))); + +// match self.axis { +// Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0), +// Axis::Vertical => child_origin += vec2f(0.0, child.size().y()), +// } + +// if can_resize && children_iter.peek().is_some() { +// cx.scene().push_stacking_context(None, None); + +// let handle_origin = match self.axis { +// Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0), +// Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.), +// }; + +// let handle_bounds = match self.axis { +// Axis::Horizontal => Bounds::new( +// handle_origin, +// vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()), +// ), +// Axis::Vertical => Bounds::new( +// handle_origin, +// vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE), +// ), +// }; + +// let style = match self.axis { +// Axis::Horizontal => CursorStyle::ResizeLeftRight, +// Axis::Vertical => CursorStyle::ResizeUpDown, +// }; + +// cx.scene().push_cursor_region(CursorRegion { +// bounds: handle_bounds, +// style, +// }); + +// enum ResizeHandle {} +// let mut mouse_region = MouseRegion::new::( +// cx.view_id(), +// self.basis + ix, +// handle_bounds, +// ); +// mouse_region = mouse_region +// .on_drag( +// MouseButton::Left, +// Self::handle_resize( +// self.flexes.clone(), +// self.axis, +// ix, +// child_start, +// visible_bounds.clone(), +// ), +// ) +// .on_click(MouseButton::Left, { +// let flexes = self.flexes.clone(); +// move |e, v: &mut Workspace, cx| { +// if e.click_count >= 2 { +// let mut borrow = flexes.borrow_mut(); +// *borrow = vec![1.; borrow.len()]; +// v.schedule_serialize(cx); +// cx.notify(); +// } +// } +// }); +// cx.scene().push_mouse_region(mouse_region); + +// cx.scene().pop_stacking_context(); +// } +// } + +// if overflowing { +// cx.scene().pop_layer(); +// } +// } + +// fn rect_for_text_range( +// &self, +// range_utf16: Range, +// _: Bounds, +// _: Bounds, +// _: &Self::LayoutState, +// _: &Self::PaintState, +// view: &Workspace, +// cx: &ViewContext, +// ) -> Option> { +// self.children +// .iter() +// .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx)) +// } + +// fn debug( +// &self, +// bounds: Bounds, +// _: &Self::LayoutState, +// _: &Self::PaintState, +// view: &Workspace, +// cx: &ViewContext, +// ) -> json::Value { +// serde_json::json!({ +// "type": "PaneAxis", +// "bounds": bounds.to_json(), +// "axis": self.axis.to_json(), +// "flexes": *self.flexes.borrow(), +// "children": self.children.iter().map(|child| child.debug(view, cx)).collect::>() +// }) +// } +// } +// } diff --git a/crates/workspace2/src/persistence/model.rs b/crates/workspace2/src/persistence/model.rs new file mode 100644 index 0000000000..2e28dabffb --- /dev/null +++ b/crates/workspace2/src/persistence/model.rs @@ -0,0 +1,340 @@ +use crate::{ + item::ItemHandle, Axis, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId, +}; +use anyhow::{Context, Result}; +use async_recursion::async_recursion; +use db2::sqlez::{ + bindable::{Bind, Column, StaticColumnCount}, + statement::Statement, +}; +use gpui2::{AsyncAppContext, Handle, Task, View, WeakView, WindowBounds}; +use project2::Project; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; +use util::ResultExt; +use uuid::Uuid; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WorkspaceLocation(Arc>); + +impl WorkspaceLocation { + pub fn paths(&self) -> Arc> { + self.0.clone() + } +} + +impl, T: IntoIterator> From for WorkspaceLocation { + fn from(iterator: T) -> Self { + let mut roots = iterator + .into_iter() + .map(|p| p.as_ref().to_path_buf()) + .collect::>(); + roots.sort(); + Self(Arc::new(roots)) + } +} + +impl StaticColumnCount for WorkspaceLocation {} +impl Bind for &WorkspaceLocation { + fn bind(&self, statement: &Statement, start_index: i32) -> Result { + bincode::serialize(&self.0) + .expect("Bincode serialization of paths should not fail") + .bind(statement, start_index) + } +} + +impl Column for WorkspaceLocation { + fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { + let blob = statement.column_blob(start_index)?; + Ok(( + WorkspaceLocation(bincode::deserialize(blob).context("Bincode failed")?), + start_index + 1, + )) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct SerializedWorkspace { + pub id: WorkspaceId, + pub location: WorkspaceLocation, + pub center_group: SerializedPaneGroup, + pub bounds: Option, + pub display: Option, + pub docks: DockStructure, +} + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct DockStructure { + pub(crate) left: DockData, + pub(crate) right: DockData, + pub(crate) bottom: DockData, +} + +impl Column for DockStructure { + fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { + let (left, next_index) = DockData::column(statement, start_index)?; + let (right, next_index) = DockData::column(statement, next_index)?; + let (bottom, next_index) = DockData::column(statement, next_index)?; + Ok(( + DockStructure { + left, + right, + bottom, + }, + next_index, + )) + } +} + +impl Bind for DockStructure { + fn bind(&self, statement: &Statement, start_index: i32) -> Result { + let next_index = statement.bind(&self.left, start_index)?; + let next_index = statement.bind(&self.right, next_index)?; + statement.bind(&self.bottom, next_index) + } +} + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct DockData { + pub(crate) visible: bool, + pub(crate) active_panel: Option, + pub(crate) zoom: bool, +} + +impl Column for DockData { + fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { + let (visible, next_index) = Option::::column(statement, start_index)?; + let (active_panel, next_index) = Option::::column(statement, next_index)?; + let (zoom, next_index) = Option::::column(statement, next_index)?; + Ok(( + DockData { + visible: visible.unwrap_or(false), + active_panel, + zoom: zoom.unwrap_or(false), + }, + next_index, + )) + } +} + +impl Bind for DockData { + fn bind(&self, statement: &Statement, start_index: i32) -> Result { + let next_index = statement.bind(&self.visible, start_index)?; + let next_index = statement.bind(&self.active_panel, next_index)?; + statement.bind(&self.zoom, next_index) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum SerializedPaneGroup { + Group { + axis: Axis, + flexes: Option>, + children: Vec, + }, + Pane(SerializedPane), +} + +#[cfg(test)] +impl Default for SerializedPaneGroup { + fn default() -> Self { + Self::Pane(SerializedPane { + children: vec![SerializedItem::default()], + active: false, + }) + } +} + +impl SerializedPaneGroup { + #[async_recursion(?Send)] + pub(crate) async fn deserialize( + self, + project: &Handle, + workspace_id: WorkspaceId, + workspace: &WeakView, + cx: &mut AsyncAppContext, + ) -> Option<(Member, Option>, Vec>>)> { + match self { + SerializedPaneGroup::Group { + axis, + children, + flexes, + } => { + let mut current_active_pane = None; + let mut members = Vec::new(); + let mut items = Vec::new(); + for child in children { + if let Some((new_member, active_pane, new_items)) = child + .deserialize(project, workspace_id, workspace, cx) + .await + { + members.push(new_member); + items.extend(new_items); + current_active_pane = current_active_pane.or(active_pane); + } + } + + if members.is_empty() { + return None; + } + + if members.len() == 1 { + return Some((members.remove(0), current_active_pane, items)); + } + + Some(( + Member::Axis(PaneAxis::load(axis, members, flexes)), + current_active_pane, + items, + )) + } + SerializedPaneGroup::Pane(serialized_pane) => { + let pane = workspace + .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade()) + .log_err()?; + let active = serialized_pane.active; + let new_items = serialized_pane + .deserialize_to(project, &pane, workspace_id, workspace, cx) + .await + .log_err()?; + + if pane + .read_with(cx, |pane, _| pane.items_len() != 0) + .log_err()? + { + let pane = pane.upgrade()?; + Some((Member::Pane(pane.clone()), active.then(|| pane), new_items)) + } else { + let pane = pane.upgrade()?; + workspace + .update(cx, |workspace, cx| workspace.force_remove_pane(&pane, cx)) + .log_err()?; + None + } + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Default, Clone)] +pub struct SerializedPane { + pub(crate) active: bool, + pub(crate) children: Vec, +} + +impl SerializedPane { + pub fn new(children: Vec, active: bool) -> Self { + SerializedPane { children, active } + } + + pub async fn deserialize_to( + &self, + project: &Handle, + pane: &WeakView, + workspace_id: WorkspaceId, + workspace: &WeakView, + cx: &mut AsyncAppContext, + ) -> Result>>> { + let mut items = Vec::new(); + let mut active_item_index = None; + for (index, item) in self.children.iter().enumerate() { + let project = project.clone(); + let item_handle = pane + .update(cx, |_, cx| { + if let Some(deserializer) = cx.global::().get(&item.kind) { + deserializer(project, workspace.clone(), workspace_id, item.item_id, cx) + } else { + Task::ready(Err(anyhow::anyhow!( + "Deserializer does not exist for item kind: {}", + item.kind + ))) + } + })? + .await + .log_err(); + + items.push(item_handle.clone()); + + if let Some(item_handle) = item_handle { + pane.update(cx, |pane, cx| { + pane.add_item(item_handle.clone(), true, true, None, cx); + })?; + } + + if item.active { + active_item_index = Some(index); + } + } + + if let Some(active_item_index) = active_item_index { + pane.update(cx, |pane, cx| { + pane.activate_item(active_item_index, false, false, cx); + })?; + } + + anyhow::Ok(items) + } +} + +pub type GroupId = i64; +pub type PaneId = i64; +pub type ItemId = usize; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct SerializedItem { + pub kind: Arc, + pub item_id: ItemId, + pub active: bool, +} + +impl SerializedItem { + pub fn new(kind: impl AsRef, item_id: ItemId, active: bool) -> Self { + Self { + kind: Arc::from(kind.as_ref()), + item_id, + active, + } + } +} + +#[cfg(test)] +impl Default for SerializedItem { + fn default() -> Self { + SerializedItem { + kind: Arc::from("Terminal"), + item_id: 100000, + active: false, + } + } +} + +impl StaticColumnCount for SerializedItem { + fn column_count() -> usize { + 3 + } +} +impl Bind for &SerializedItem { + fn bind(&self, statement: &Statement, start_index: i32) -> Result { + let next_index = statement.bind(&self.kind, start_index)?; + let next_index = statement.bind(&self.item_id, next_index)?; + statement.bind(&self.active, next_index) + } +} + +impl Column for SerializedItem { + fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { + let (kind, next_index) = Arc::::column(statement, start_index)?; + let (item_id, next_index) = ItemId::column(statement, next_index)?; + let (active, next_index) = bool::column(statement, next_index)?; + Ok(( + SerializedItem { + kind, + item_id, + active, + }, + next_index, + )) + } +} diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs new file mode 100644 index 0000000000..3731094b26 --- /dev/null +++ b/crates/workspace2/src/workspace2.rs @@ -0,0 +1,5535 @@ +// pub mod dock; +pub mod item; +// pub mod notifications; +pub mod pane; +pub mod pane_group; +mod persistence; +pub mod searchable; +// pub mod shared_screen; +// mod status_bar; +mod toolbar; +mod workspace_settings; + +use anyhow::{anyhow, Result}; +// use call2::ActiveCall; +// use client2::{ +// proto::{self, PeerId}, +// Client, Status, TypedEnvelope, UserStore, +// }; +// use collections::{hash_map, HashMap, HashSet}; +// use futures::{ +// channel::{mpsc, oneshot}, +// future::try_join_all, +// FutureExt, StreamExt, +// }; +// use gpui2::{ +// actions, +// elements::*, +// geometry::{ +// rect::RectF, +// vector::{vec2f, Vector2F}, +// }, +// impl_actions, +// platform::{ +// CursorStyle, ModifiersChangedEvent, MouseButton, PathPromptOptions, Platform, PromptLevel, +// WindowBounds, WindowOptions, +// }, +// AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AnyWindowHandle, AppContext, AsyncAppContext, +// Entity, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, +// View, WeakViewHandle, WindowContext, WindowHandle, +// }; +// use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; +// use itertools::Itertools; +// use language2::{LanguageRegistry, Rope}; +// use node_runtime::NodeRuntime;// // + +use futures::channel::oneshot; +// use crate::{ +// notifications::{simple_message_notification::MessageNotification, NotificationTracker}, +// persistence::model::{ +// DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace, +// }, +// }; +// use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle}; +// use lazy_static::lazy_static; +// use notifications::{NotificationHandle, NotifyResultExt}; +pub use pane::*; +pub use pane_group::*; +// use persistence::{model::SerializedItem, DB}; +// pub use persistence::{ +// model::{ItemId, WorkspaceLocation}, +// WorkspaceDb, DB as WORKSPACE_DB, +// }; +// use postage::prelude::Stream; +// use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; +// use serde::Deserialize; +// use shared_screen::SharedScreen; +// use status_bar::StatusBar; +// pub use status_bar::StatusItemView; +// use theme::{Theme, ThemeSettings}; +pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; +// use util::ResultExt; +// pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; + +// lazy_static! { +// static ref ZED_WINDOW_SIZE: Option = env::var("ZED_WINDOW_SIZE") +// .ok() +// .as_deref() +// .and_then(parse_pixel_position_env_var); +// static ref ZED_WINDOW_POSITION: Option = env::var("ZED_WINDOW_POSITION") +// .ok() +// .as_deref() +// .and_then(parse_pixel_position_env_var); +// } + +// pub trait Modal: View { +// fn has_focus(&self) -> bool; +// fn dismiss_on_event(event: &Self::Event) -> bool; +// } + +// trait ModalHandle { +// fn as_any(&self) -> &AnyViewHandle; +// fn has_focus(&self, cx: &WindowContext) -> bool; +// } + +// impl ModalHandle for View { +// fn as_any(&self) -> &AnyViewHandle { +// self +// } + +// fn has_focus(&self, cx: &WindowContext) -> bool { +// self.read(cx).has_focus() +// } +// } + +// #[derive(Clone, PartialEq)] +// pub struct RemoveWorktreeFromProject(pub WorktreeId); + +// actions!( +// workspace, +// [ +// Open, +// NewFile, +// NewWindow, +// CloseWindow, +// CloseInactiveTabsAndPanes, +// AddFolderToProject, +// Unfollow, +// SaveAs, +// ReloadActiveItem, +// ActivatePreviousPane, +// ActivateNextPane, +// FollowNextCollaborator, +// NewTerminal, +// NewCenterTerminal, +// ToggleTerminalFocus, +// NewSearch, +// Feedback, +// Restart, +// Welcome, +// ToggleZoom, +// ToggleLeftDock, +// ToggleRightDock, +// ToggleBottomDock, +// CloseAllDocks, +// ] +// ); + +// #[derive(Clone, PartialEq)] +// pub struct OpenPaths { +// pub paths: Vec, +// } + +// #[derive(Clone, Deserialize, PartialEq)] +// pub struct ActivatePane(pub usize); + +// #[derive(Clone, Deserialize, PartialEq)] +// pub struct ActivatePaneInDirection(pub SplitDirection); + +// #[derive(Clone, Deserialize, PartialEq)] +// pub struct SwapPaneInDirection(pub SplitDirection); + +// #[derive(Clone, Deserialize, PartialEq)] +// pub struct NewFileInDirection(pub SplitDirection); + +// #[derive(Clone, PartialEq, Debug, Deserialize)] +// #[serde(rename_all = "camelCase")] +// pub struct SaveAll { +// pub save_intent: Option, +// } + +// #[derive(Clone, PartialEq, Debug, Deserialize)] +// #[serde(rename_all = "camelCase")] +// pub struct Save { +// pub save_intent: Option, +// } + +// #[derive(Clone, PartialEq, Debug, Deserialize, Default)] +// #[serde(rename_all = "camelCase")] +// pub struct CloseAllItemsAndPanes { +// pub save_intent: Option, +// } + +// #[derive(Deserialize)] +// pub struct Toast { +// id: usize, +// msg: Cow<'static, str>, +// #[serde(skip)] +// on_click: Option<(Cow<'static, str>, Arc)>, +// } + +// impl Toast { +// pub fn new>>(id: usize, msg: I) -> Self { +// Toast { +// id, +// msg: msg.into(), +// on_click: None, +// } +// } + +// pub fn on_click(mut self, message: M, on_click: F) -> Self +// where +// M: Into>, +// F: Fn(&mut WindowContext) + 'static, +// { +// self.on_click = Some((message.into(), Arc::new(on_click))); +// self +// } +// } + +// impl PartialEq for Toast { +// fn eq(&self, other: &Self) -> bool { +// self.id == other.id +// && self.msg == other.msg +// && self.on_click.is_some() == other.on_click.is_some() +// } +// } + +// impl Clone for Toast { +// fn clone(&self) -> Self { +// Toast { +// id: self.id, +// msg: self.msg.to_owned(), +// on_click: self.on_click.clone(), +// } +// } +// } + +// #[derive(Clone, Deserialize, PartialEq)] +// pub struct OpenTerminal { +// pub working_directory: PathBuf, +// } + +// impl_actions!( +// workspace, +// [ +// ActivatePane, +// ActivatePaneInDirection, +// SwapPaneInDirection, +// NewFileInDirection, +// Toast, +// OpenTerminal, +// SaveAll, +// Save, +// CloseAllItemsAndPanes, +// ] +// ); + +pub type WorkspaceId = i64; + +// pub fn init_settings(cx: &mut AppContext) { +// settings::register::(cx); +// settings::register::(cx); +// } + +// pub fn init(app_state: Arc, cx: &mut AppContext) { +// init_settings(cx); +// pane::init(cx); +// notifications::init(cx); + +// cx.add_global_action({ +// let app_state = Arc::downgrade(&app_state); +// move |_: &Open, cx: &mut AppContext| { +// let mut paths = cx.prompt_for_paths(PathPromptOptions { +// files: true, +// directories: true, +// multiple: true, +// }); + +// if let Some(app_state) = app_state.upgrade() { +// cx.spawn(move |mut cx| async move { +// if let Some(paths) = paths.recv().await.flatten() { +// cx.update(|cx| { +// open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx) +// }); +// } +// }) +// .detach(); +// } +// } +// }); +// cx.add_async_action(Workspace::open); + +// cx.add_async_action(Workspace::follow_next_collaborator); +// cx.add_async_action(Workspace::close); +// cx.add_async_action(Workspace::close_inactive_items_and_panes); +// cx.add_async_action(Workspace::close_all_items_and_panes); +// cx.add_global_action(Workspace::close_global); +// cx.add_global_action(restart); +// cx.add_async_action(Workspace::save_all); +// cx.add_action(Workspace::add_folder_to_project); +// cx.add_action( +// |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext| { +// let pane = workspace.active_pane().clone(); +// workspace.unfollow(&pane, cx); +// }, +// ); +// cx.add_action( +// |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { +// workspace +// .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) +// .detach_and_log_err(cx); +// }, +// ); +// cx.add_action( +// |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext| { +// workspace +// .save_active_item(SaveIntent::SaveAs, cx) +// .detach_and_log_err(cx); +// }, +// ); +// cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { +// workspace.activate_previous_pane(cx) +// }); +// cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { +// workspace.activate_next_pane(cx) +// }); + +// cx.add_action( +// |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| { +// workspace.activate_pane_in_direction(action.0, cx) +// }, +// ); + +// cx.add_action( +// |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| { +// workspace.swap_pane_in_direction(action.0, cx) +// }, +// ); + +// cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| { +// workspace.toggle_dock(DockPosition::Left, cx); +// }); +// cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| { +// workspace.toggle_dock(DockPosition::Right, cx); +// }); +// cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| { +// workspace.toggle_dock(DockPosition::Bottom, cx); +// }); +// cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| { +// workspace.close_all_docks(cx); +// }); +// cx.add_action(Workspace::activate_pane_at_index); +// cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { +// workspace.reopen_closed_item(cx).detach(); +// }); +// cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| { +// workspace +// .go_back(workspace.active_pane().downgrade(), cx) +// .detach(); +// }); +// cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| { +// workspace +// .go_forward(workspace.active_pane().downgrade(), cx) +// .detach(); +// }); + +// cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| { +// cx.spawn(|workspace, mut cx| async move { +// let err = install_cli::install_cli(&cx) +// .await +// .context("Failed to create CLI symlink"); + +// workspace.update(&mut cx, |workspace, cx| { +// if matches!(err, Err(_)) { +// err.notify_err(workspace, cx); +// } else { +// workspace.show_notification(1, cx, |cx| { +// cx.add_view(|_| { +// MessageNotification::new("Successfully installed the `zed` binary") +// }) +// }); +// } +// }) +// }) +// .detach(); +// }); +// } + +type ProjectItemBuilders = + HashMap, AnyHandle, &mut ViewContext) -> Box>; +pub fn register_project_item(cx: &mut AppContext) { + cx.update_default_global(|builders: &mut ProjectItemBuilders, _| { + builders.insert(TypeId::of::(), |project, model, cx| { + let item = model.downcast::().unwrap(); + Box::new(cx.add_view(|cx| I::for_project_item(project, item, cx))) + }); + }); +} + +type FollowableItemBuilder = fn( + View, + View, + ViewId, + &mut Option, + &mut AppContext, +) -> Option>>>; +type FollowableItemBuilders = HashMap< + TypeId, + ( + FollowableItemBuilder, + fn(&AnyView) -> Box, + ), +>; +pub fn register_followable_item(cx: &mut AppContext) { + cx.update_default_global(|builders: &mut FollowableItemBuilders, _| { + builders.insert( + TypeId::of::(), + ( + |pane, workspace, id, state, cx| { + I::from_state_proto(pane, workspace, id, state, cx).map(|task| { + cx.foreground() + .spawn(async move { Ok(Box::new(task.await?) as Box<_>) }) + }) + }, + |this| Box::new(this.clone().downcast::().unwrap()), + ), + ); + }); +} + +type ItemDeserializers = HashMap< + Arc, + fn( + Handle, + WeakView, + WorkspaceId, + ItemId, + &mut ViewContext, + ) -> Task>>, +>; +pub fn register_deserializable_item(cx: &mut AppContext) { + cx.update_default_global(|deserializers: &mut ItemDeserializers, _cx| { + if let Some(serialized_item_kind) = I::serialized_item_kind() { + deserializers.insert( + Arc::from(serialized_item_kind), + |project, workspace, workspace_id, item_id, cx| { + let task = I::deserialize(project, workspace, workspace_id, item_id, cx); + cx.foreground() + .spawn(async { Ok(Box::new(task.await?) as Box<_>) }) + }, + ); + } + }); +} + +pub struct AppState { + pub languages: Arc, + pub client: Arc, + pub user_store: Handle, + pub workspace_store: Handle, + pub fs: Arc, + pub build_window_options: + fn(Option, Option, &MainThread) -> WindowOptions, + pub initialize_workspace: + fn(WeakHandle, bool, Arc, AsyncAppContext) -> Task>, + pub node_runtime: Arc, +} + +pub struct WorkspaceStore { + workspaces: HashSet>, + followers: Vec, + client: Arc, + _subscriptions: Vec, +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +struct Follower { + project_id: Option, + peer_id: PeerId, +} + +// impl AppState { +// #[cfg(any(test, feature = "test-support"))] +// pub fn test(cx: &mut AppContext) -> Arc { +// use node_runtime::FakeNodeRuntime; +// use settings::SettingsStore; + +// if !cx.has_global::() { +// cx.set_global(SettingsStore::test(cx)); +// } + +// let fs = fs::FakeFs::new(cx.background().clone()); +// let languages = Arc::new(LanguageRegistry::test()); +// let http_client = util::http::FakeHttpClient::with_404_response(); +// let client = Client::new(http_client.clone(), cx); +// let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); +// let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx)); + +// theme::init((), cx); +// client::init(&client, cx); +// crate::init_settings(cx); + +// Arc::new(Self { +// client, +// fs, +// languages, +// user_store, +// // channel_store, +// workspace_store, +// node_runtime: FakeNodeRuntime::new(), +// initialize_workspace: |_, _, _, _| Task::ready(Ok(())), +// build_window_options: |_, _, _| Default::default(), +// }) +// } +// } + +struct DelayedDebouncedEditAction { + task: Option>, + cancel_channel: Option>, +} + +impl DelayedDebouncedEditAction { + fn new() -> DelayedDebouncedEditAction { + DelayedDebouncedEditAction { + task: None, + cancel_channel: None, + } + } + + fn fire_new(&mut self, delay: Duration, cx: &mut ViewContext, func: F) + where + F: 'static + FnOnce(&mut Workspace, &mut ViewContext) -> Task>, + { + if let Some(channel) = self.cancel_channel.take() { + _ = channel.send(()); + } + + let (sender, mut receiver) = oneshot::channel::<()>(); + self.cancel_channel = Some(sender); + + let previous_task = self.task.take(); + self.task = Some(cx.spawn(|workspace, mut cx| async move { + let mut timer = cx.background().timer(delay).fuse(); + if let Some(previous_task) = previous_task { + previous_task.await; + } + + futures::select_biased! { + _ = receiver => return, + _ = timer => {} + } + + if let Some(result) = workspace + .update(&mut cx, |workspace, cx| (func)(workspace, cx)) + .log_err() + { + result.await.log_err(); + } + })); + } +} + +// pub enum Event { +// PaneAdded(View), +// ContactRequestedJoin(u64), +// } + +pub struct Workspace { + weak_self: WeakHandle, + // modal: Option, + // zoomed: Option, + // zoomed_position: Option, + // center: PaneGroup, + // left_dock: View, + // bottom_dock: View, + // right_dock: View, + panes: Vec>, + // panes_by_item: HashMap>, + // active_pane: View, + last_active_center_pane: Option>, + // last_active_view_id: Option, + // status_bar: View, + // titlebar_item: Option, + // notifications: Vec<(TypeId, usize, Box)>, + project: Handle, + // follower_states: HashMap, FollowerState>, + // last_leaders_by_pane: HashMap, PeerId>, + // window_edited: bool, + // active_call: Option<(ModelHandle, Vec)>, + // leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, + // database_id: WorkspaceId, + app_state: Arc, + // subscriptions: Vec, + // _apply_leader_updates: Task>, + // _observe_current_user: Task>, + // _schedule_serialize: Option>, + // pane_history_timestamp: Arc, +} + +// struct ActiveModal { +// view: Box, +// previously_focused_view_id: Option, +// } + +// #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +// pub struct ViewId { +// pub creator: PeerId, +// pub id: u64, +// } + +#[derive(Default)] +struct FollowerState { + leader_id: PeerId, + active_view_id: Option, + items_by_leader_view_id: HashMap>, +} + +// enum WorkspaceBounds {} + +impl Workspace { + // pub fn new( + // workspace_id: WorkspaceId, + // project: ModelHandle, + // app_state: Arc, + // cx: &mut ViewContext, + // ) -> Self { + // cx.observe(&project, |_, _, cx| cx.notify()).detach(); + // cx.subscribe(&project, move |this, _, event, cx| { + // match event { + // project::Event::RemoteIdChanged(_) => { + // this.update_window_title(cx); + // } + + // project::Event::CollaboratorLeft(peer_id) => { + // this.collaborator_left(*peer_id, cx); + // } + + // project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded => { + // this.update_window_title(cx); + // this.serialize_workspace(cx); + // } + + // project::Event::DisconnectedFromHost => { + // this.update_window_edited(cx); + // cx.blur(); + // } + + // project::Event::Closed => { + // cx.remove_window(); + // } + + // project::Event::DeletedEntry(entry_id) => { + // for pane in this.panes.iter() { + // pane.update(cx, |pane, cx| { + // pane.handle_deleted_project_item(*entry_id, cx) + // }); + // } + // } + + // project::Event::Notification(message) => this.show_notification(0, cx, |cx| { + // cx.add_view(|_| MessageNotification::new(message.clone())) + // }), + + // _ => {} + // } + // cx.notify() + // }) + // .detach(); + + // let weak_handle = cx.weak_handle(); + // let pane_history_timestamp = Arc::new(AtomicUsize::new(0)); + + // let center_pane = cx.add_view(|cx| { + // Pane::new( + // weak_handle.clone(), + // project.clone(), + // pane_history_timestamp.clone(), + // cx, + // ) + // }); + // cx.subscribe(¢er_pane, Self::handle_pane_event).detach(); + // cx.focus(¢er_pane); + // cx.emit(Event::PaneAdded(center_pane.clone())); + + // app_state.workspace_store.update(cx, |store, _| { + // store.workspaces.insert(weak_handle.clone()); + // }); + + // let mut current_user = app_state.user_store.read(cx).watch_current_user(); + // let mut connection_status = app_state.client.status(); + // let _observe_current_user = cx.spawn(|this, mut cx| async move { + // current_user.recv().await; + // connection_status.recv().await; + // let mut stream = + // Stream::map(current_user, drop).merge(Stream::map(connection_status, drop)); + + // while stream.recv().await.is_some() { + // this.update(&mut cx, |_, cx| cx.notify())?; + // } + // anyhow::Ok(()) + // }); + + // // All leader updates are enqueued and then processed in a single task, so + // // that each asynchronous operation can be run in order. + // let (leader_updates_tx, mut leader_updates_rx) = + // mpsc::unbounded::<(PeerId, proto::UpdateFollowers)>(); + // let _apply_leader_updates = cx.spawn(|this, mut cx| async move { + // while let Some((leader_id, update)) = leader_updates_rx.next().await { + // Self::process_leader_update(&this, leader_id, update, &mut cx) + // .await + // .log_err(); + // } + + // Ok(()) + // }); + + // cx.emit_global(WorkspaceCreated(weak_handle.clone())); + + // let left_dock = cx.add_view(|_| Dock::new(DockPosition::Left)); + // let bottom_dock = cx.add_view(|_| Dock::new(DockPosition::Bottom)); + // let right_dock = cx.add_view(|_| Dock::new(DockPosition::Right)); + // let left_dock_buttons = + // cx.add_view(|cx| PanelButtons::new(left_dock.clone(), weak_handle.clone(), cx)); + // let bottom_dock_buttons = + // cx.add_view(|cx| PanelButtons::new(bottom_dock.clone(), weak_handle.clone(), cx)); + // let right_dock_buttons = + // cx.add_view(|cx| PanelButtons::new(right_dock.clone(), weak_handle.clone(), cx)); + // let status_bar = cx.add_view(|cx| { + // let mut status_bar = StatusBar::new(¢er_pane.clone(), cx); + // status_bar.add_left_item(left_dock_buttons, cx); + // status_bar.add_right_item(right_dock_buttons, cx); + // status_bar.add_right_item(bottom_dock_buttons, cx); + // status_bar + // }); + + // cx.update_default_global::, _, _>(|drag_and_drop, _| { + // drag_and_drop.register_container(weak_handle.clone()); + // }); + + // let mut active_call = None; + // if cx.has_global::>() { + // let call = cx.global::>().clone(); + // let mut subscriptions = Vec::new(); + // subscriptions.push(cx.subscribe(&call, Self::on_active_call_event)); + // active_call = Some((call, subscriptions)); + // } + + // let subscriptions = vec![ + // cx.observe_fullscreen(|_, _, cx| cx.notify()), + // cx.observe_window_activation(Self::on_window_activation_changed), + // cx.observe_window_bounds(move |_, mut bounds, display, cx| { + // // Transform fixed bounds to be stored in terms of the containing display + // if let WindowBounds::Fixed(mut window_bounds) = bounds { + // if let Some(screen) = cx.platform().screen_by_id(display) { + // let screen_bounds = screen.bounds(); + // window_bounds + // .set_origin_x(window_bounds.origin_x() - screen_bounds.origin_x()); + // window_bounds + // .set_origin_y(window_bounds.origin_y() - screen_bounds.origin_y()); + // bounds = WindowBounds::Fixed(window_bounds); + // } + // } + + // cx.background() + // .spawn(DB.set_window_bounds(workspace_id, bounds, display)) + // .detach_and_log_err(cx); + // }), + // cx.observe(&left_dock, |this, _, cx| { + // this.serialize_workspace(cx); + // cx.notify(); + // }), + // cx.observe(&bottom_dock, |this, _, cx| { + // this.serialize_workspace(cx); + // cx.notify(); + // }), + // cx.observe(&right_dock, |this, _, cx| { + // this.serialize_workspace(cx); + // cx.notify(); + // }), + // ]; + + // cx.defer(|this, cx| this.update_window_title(cx)); + // Workspace { + // weak_self: weak_handle.clone(), + // modal: None, + // zoomed: None, + // zoomed_position: None, + // center: PaneGroup::new(center_pane.clone()), + // panes: vec![center_pane.clone()], + // panes_by_item: Default::default(), + // active_pane: center_pane.clone(), + // last_active_center_pane: Some(center_pane.downgrade()), + // last_active_view_id: None, + // status_bar, + // titlebar_item: None, + // notifications: Default::default(), + // left_dock, + // bottom_dock, + // right_dock, + // project: project.clone(), + // follower_states: Default::default(), + // last_leaders_by_pane: Default::default(), + // window_edited: false, + // active_call, + // database_id: workspace_id, + // app_state, + // _observe_current_user, + // _apply_leader_updates, + // _schedule_serialize: None, + // leader_updates_tx, + // subscriptions, + // pane_history_timestamp, + // } + // } + + // fn new_local( + // abs_paths: Vec, + // app_state: Arc, + // requesting_window: Option>, + // cx: &mut AppContext, + // ) -> Task<( + // WeakViewHandle, + // Vec, anyhow::Error>>>, + // )> { + // let project_handle = Project::local( + // app_state.client.clone(), + // app_state.node_runtime.clone(), + // app_state.user_store.clone(), + // app_state.languages.clone(), + // app_state.fs.clone(), + // cx, + // ); + + // cx.spawn(|mut cx| async move { + // let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice()); + + // let paths_to_open = Arc::new(abs_paths); + + // // Get project paths for all of the abs_paths + // let mut worktree_roots: HashSet> = Default::default(); + // let mut project_paths: Vec<(PathBuf, Option)> = + // Vec::with_capacity(paths_to_open.len()); + // for path in paths_to_open.iter().cloned() { + // if let Some((worktree, project_entry)) = cx + // .update(|cx| { + // Workspace::project_path_for_path(project_handle.clone(), &path, true, cx) + // }) + // .await + // .log_err() + // { + // worktree_roots.insert(worktree.read_with(&mut cx, |tree, _| tree.abs_path())); + // project_paths.push((path, Some(project_entry))); + // } else { + // project_paths.push((path, None)); + // } + // } + + // let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() { + // serialized_workspace.id + // } else { + // DB.next_id().await.unwrap_or(0) + // }; + + // let window = if let Some(window) = requesting_window { + // window.replace_root(&mut cx, |cx| { + // Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) + // }); + // window + // } else { + // { + // let window_bounds_override = window_bounds_env_override(&cx); + // let (bounds, display) = if let Some(bounds) = window_bounds_override { + // (Some(bounds), None) + // } else { + // serialized_workspace + // .as_ref() + // .and_then(|serialized_workspace| { + // let display = serialized_workspace.display?; + // let mut bounds = serialized_workspace.bounds?; + + // // Stored bounds are relative to the containing display. + // // So convert back to global coordinates if that screen still exists + // if let WindowBounds::Fixed(mut window_bounds) = bounds { + // if let Some(screen) = cx.platform().screen_by_id(display) { + // let screen_bounds = screen.bounds(); + // window_bounds.set_origin_x( + // window_bounds.origin_x() + screen_bounds.origin_x(), + // ); + // window_bounds.set_origin_y( + // window_bounds.origin_y() + screen_bounds.origin_y(), + // ); + // bounds = WindowBounds::Fixed(window_bounds); + // } else { + // // Screen no longer exists. Return none here. + // return None; + // } + // } + + // Some((bounds, display)) + // }) + // .unzip() + // }; + + // // Use the serialized workspace to construct the new window + // cx.add_window( + // (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), + // |cx| { + // Workspace::new( + // workspace_id, + // project_handle.clone(), + // app_state.clone(), + // cx, + // ) + // }, + // ) + // } + // }; + + // // We haven't yielded the main thread since obtaining the window handle, + // // so the window exists. + // let workspace = window.root(&cx).unwrap(); + + // (app_state.initialize_workspace)( + // workspace.downgrade(), + // serialized_workspace.is_some(), + // app_state.clone(), + // cx.clone(), + // ) + // .await + // .log_err(); + + // window.update(&mut cx, |cx| cx.activate_window()); + + // let workspace = workspace.downgrade(); + // notify_if_database_failed(&workspace, &mut cx); + // let opened_items = open_items( + // serialized_workspace, + // &workspace, + // project_paths, + // app_state, + // cx, + // ) + // .await + // .unwrap_or_default(); + + // (workspace, opened_items) + // }) + // } + + // pub fn weak_handle(&self) -> WeakViewHandle { + // self.weak_self.clone() + // } + + // pub fn left_dock(&self) -> &View { + // &self.left_dock + // } + + // pub fn bottom_dock(&self) -> &View { + // &self.bottom_dock + // } + + // pub fn right_dock(&self) -> &View { + // &self.right_dock + // } + + // pub fn add_panel(&mut self, panel: View, cx: &mut ViewContext) + // where + // T::Event: std::fmt::Debug, + // { + // self.add_panel_with_extra_event_handler(panel, cx, |_, _, _, _| {}) + // } + + // pub fn add_panel_with_extra_event_handler( + // &mut self, + // panel: View, + // cx: &mut ViewContext, + // handler: F, + // ) where + // T::Event: std::fmt::Debug, + // F: Fn(&mut Self, &View, &T::Event, &mut ViewContext) + 'static, + // { + // let dock = match panel.position(cx) { + // DockPosition::Left => &self.left_dock, + // DockPosition::Bottom => &self.bottom_dock, + // DockPosition::Right => &self.right_dock, + // }; + + // self.subscriptions.push(cx.subscribe(&panel, { + // let mut dock = dock.clone(); + // let mut prev_position = panel.position(cx); + // move |this, panel, event, cx| { + // if T::should_change_position_on_event(event) { + // let new_position = panel.read(cx).position(cx); + // let mut was_visible = false; + // dock.update(cx, |dock, cx| { + // prev_position = new_position; + + // was_visible = dock.is_open() + // && dock + // .visible_panel() + // .map_or(false, |active_panel| active_panel.id() == panel.id()); + // dock.remove_panel(&panel, cx); + // }); + + // if panel.is_zoomed(cx) { + // this.zoomed_position = Some(new_position); + // } + + // dock = match panel.read(cx).position(cx) { + // DockPosition::Left => &this.left_dock, + // DockPosition::Bottom => &this.bottom_dock, + // DockPosition::Right => &this.right_dock, + // } + // .clone(); + // dock.update(cx, |dock, cx| { + // dock.add_panel(panel.clone(), cx); + // if was_visible { + // dock.set_open(true, cx); + // dock.activate_panel(dock.panels_len() - 1, cx); + // } + // }); + // } else if T::should_zoom_in_on_event(event) { + // dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx)); + // if !panel.has_focus(cx) { + // cx.focus(&panel); + // } + // this.zoomed = Some(panel.downgrade().into_any()); + // this.zoomed_position = Some(panel.read(cx).position(cx)); + // } else if T::should_zoom_out_on_event(event) { + // dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx)); + // if this.zoomed_position == Some(prev_position) { + // this.zoomed = None; + // this.zoomed_position = None; + // } + // cx.notify(); + // } else if T::is_focus_event(event) { + // let position = panel.read(cx).position(cx); + // this.dismiss_zoomed_items_to_reveal(Some(position), cx); + // if panel.is_zoomed(cx) { + // this.zoomed = Some(panel.downgrade().into_any()); + // this.zoomed_position = Some(position); + // } else { + // this.zoomed = None; + // this.zoomed_position = None; + // } + // this.update_active_view_for_followers(cx); + // cx.notify(); + // } else { + // handler(this, &panel, event, cx) + // } + // } + // })); + + // dock.update(cx, |dock, cx| dock.add_panel(panel, cx)); + // } + + // pub fn status_bar(&self) -> &View { + // &self.status_bar + // } + + // pub fn app_state(&self) -> &Arc { + // &self.app_state + // } + + // pub fn user_store(&self) -> &ModelHandle { + // &self.app_state.user_store + // } + + pub fn project(&self) -> &Handle { + &self.project + } + + // pub fn recent_navigation_history( + // &self, + // limit: Option, + // cx: &AppContext, + // ) -> Vec<(ProjectPath, Option)> { + // let mut abs_paths_opened: HashMap> = HashMap::default(); + // let mut history: HashMap, usize)> = HashMap::default(); + // for pane in &self.panes { + // let pane = pane.read(cx); + // pane.nav_history() + // .for_each_entry(cx, |entry, (project_path, fs_path)| { + // if let Some(fs_path) = &fs_path { + // abs_paths_opened + // .entry(fs_path.clone()) + // .or_default() + // .insert(project_path.clone()); + // } + // let timestamp = entry.timestamp; + // match history.entry(project_path) { + // hash_map::Entry::Occupied(mut entry) => { + // let (_, old_timestamp) = entry.get(); + // if ×tamp > old_timestamp { + // entry.insert((fs_path, timestamp)); + // } + // } + // hash_map::Entry::Vacant(entry) => { + // entry.insert((fs_path, timestamp)); + // } + // } + // }); + // } + + // history + // .into_iter() + // .sorted_by_key(|(_, (_, timestamp))| *timestamp) + // .map(|(project_path, (fs_path, _))| (project_path, fs_path)) + // .rev() + // .filter(|(history_path, abs_path)| { + // let latest_project_path_opened = abs_path + // .as_ref() + // .and_then(|abs_path| abs_paths_opened.get(abs_path)) + // .and_then(|project_paths| { + // project_paths + // .iter() + // .max_by(|b1, b2| b1.worktree_id.cmp(&b2.worktree_id)) + // }); + + // match latest_project_path_opened { + // Some(latest_project_path_opened) => latest_project_path_opened == history_path, + // None => true, + // } + // }) + // .take(limit.unwrap_or(usize::MAX)) + // .collect() + // } + + // fn navigate_history( + // &mut self, + // pane: WeakViewHandle, + // mode: NavigationMode, + // cx: &mut ViewContext, + // ) -> Task> { + // let to_load = if let Some(pane) = pane.upgrade(cx) { + // cx.focus(&pane); + + // pane.update(cx, |pane, cx| { + // loop { + // // Retrieve the weak item handle from the history. + // let entry = pane.nav_history_mut().pop(mode, cx)?; + + // // If the item is still present in this pane, then activate it. + // if let Some(index) = entry + // .item + // .upgrade(cx) + // .and_then(|v| pane.index_for_item(v.as_ref())) + // { + // let prev_active_item_index = pane.active_item_index(); + // pane.nav_history_mut().set_mode(mode); + // pane.activate_item(index, true, true, cx); + // pane.nav_history_mut().set_mode(NavigationMode::Normal); + + // let mut navigated = prev_active_item_index != pane.active_item_index(); + // if let Some(data) = entry.data { + // navigated |= pane.active_item()?.navigate(data, cx); + // } + + // if navigated { + // break None; + // } + // } + // // If the item is no longer present in this pane, then retrieve its + // // project path in order to reopen it. + // else { + // break pane + // .nav_history() + // .path_for_item(entry.item.id()) + // .map(|(project_path, _)| (project_path, entry)); + // } + // } + // }) + // } else { + // None + // }; + + // if let Some((project_path, entry)) = to_load { + // // If the item was no longer present, then load it again from its previous path. + // let task = self.load_path(project_path, cx); + // cx.spawn(|workspace, mut cx| async move { + // let task = task.await; + // let mut navigated = false; + // if let Some((project_entry_id, build_item)) = task.log_err() { + // let prev_active_item_id = pane.update(&mut cx, |pane, _| { + // pane.nav_history_mut().set_mode(mode); + // pane.active_item().map(|p| p.id()) + // })?; + + // pane.update(&mut cx, |pane, cx| { + // let item = pane.open_item(project_entry_id, true, cx, build_item); + // navigated |= Some(item.id()) != prev_active_item_id; + // pane.nav_history_mut().set_mode(NavigationMode::Normal); + // if let Some(data) = entry.data { + // navigated |= item.navigate(data, cx); + // } + // })?; + // } + + // if !navigated { + // workspace + // .update(&mut cx, |workspace, cx| { + // Self::navigate_history(workspace, pane, mode, cx) + // })? + // .await?; + // } + + // Ok(()) + // }) + // } else { + // Task::ready(Ok(())) + // } + // } + + // pub fn go_back( + // &mut self, + // pane: WeakViewHandle, + // cx: &mut ViewContext, + // ) -> Task> { + // self.navigate_history(pane, NavigationMode::GoingBack, cx) + // } + + // pub fn go_forward( + // &mut self, + // pane: WeakViewHandle, + // cx: &mut ViewContext, + // ) -> Task> { + // self.navigate_history(pane, NavigationMode::GoingForward, cx) + // } + + // pub fn reopen_closed_item(&mut self, cx: &mut ViewContext) -> Task> { + // self.navigate_history( + // self.active_pane().downgrade(), + // NavigationMode::ReopeningClosedItem, + // cx, + // ) + // } + + // pub fn client(&self) -> &Client { + // &self.app_state.client + // } + + // pub fn set_titlebar_item(&mut self, item: AnyViewHandle, cx: &mut ViewContext) { + // self.titlebar_item = Some(item); + // cx.notify(); + // } + + // pub fn titlebar_item(&self) -> Option { + // self.titlebar_item.clone() + // } + + // /// Call the given callback with a workspace whose project is local. + // /// + // /// If the given workspace has a local project, then it will be passed + // /// to the callback. Otherwise, a new empty window will be created. + // pub fn with_local_workspace( + // &mut self, + // cx: &mut ViewContext, + // callback: F, + // ) -> Task> + // where + // T: 'static, + // F: 'static + FnOnce(&mut Workspace, &mut ViewContext) -> T, + // { + // if self.project.read(cx).is_local() { + // Task::Ready(Some(Ok(callback(self, cx)))) + // } else { + // let task = Self::new_local(Vec::new(), self.app_state.clone(), None, cx); + // cx.spawn(|_vh, mut cx| async move { + // let (workspace, _) = task.await; + // workspace.update(&mut cx, callback) + // }) + // } + // } + + // pub fn worktrees<'a>( + // &self, + // cx: &'a AppContext, + // ) -> impl 'a + Iterator> { + // self.project.read(cx).worktrees(cx) + // } + + // pub fn visible_worktrees<'a>( + // &self, + // cx: &'a AppContext, + // ) -> impl 'a + Iterator> { + // self.project.read(cx).visible_worktrees(cx) + // } + + // pub fn worktree_scans_complete(&self, cx: &AppContext) -> impl Future + 'static { + // let futures = self + // .worktrees(cx) + // .filter_map(|worktree| worktree.read(cx).as_local()) + // .map(|worktree| worktree.scan_complete()) + // .collect::>(); + // async move { + // for future in futures { + // future.await; + // } + // } + // } + + // pub fn close_global(_: &CloseWindow, cx: &mut AppContext) { + // cx.spawn(|mut cx| async move { + // let window = cx + // .windows() + // .into_iter() + // .find(|window| window.is_active(&cx).unwrap_or(false)); + // if let Some(window) = window { + // //This can only get called when the window's project connection has been lost + // //so we don't need to prompt the user for anything and instead just close the window + // window.remove(&mut cx); + // } + // }) + // .detach(); + // } + + // pub fn close( + // &mut self, + // _: &CloseWindow, + // cx: &mut ViewContext, + // ) -> Option>> { + // let window = cx.window(); + // let prepare = self.prepare_to_close(false, cx); + // Some(cx.spawn(|_, mut cx| async move { + // if prepare.await? { + // window.remove(&mut cx); + // } + // Ok(()) + // })) + // } + + // pub fn prepare_to_close( + // &mut self, + // quitting: bool, + // cx: &mut ViewContext, + // ) -> Task> { + // let active_call = self.active_call().cloned(); + // let window = cx.window(); + + // cx.spawn(|this, mut cx| async move { + // let workspace_count = cx + // .windows() + // .into_iter() + // .filter(|window| window.root_is::()) + // .count(); + + // if let Some(active_call) = active_call { + // if !quitting + // && workspace_count == 1 + // && active_call.read_with(&cx, |call, _| call.room().is_some()) + // { + // let answer = window.prompt( + // PromptLevel::Warning, + // "Do you want to leave the current call?", + // &["Close window and hang up", "Cancel"], + // &mut cx, + // ); + + // if let Some(mut answer) = answer { + // if answer.next().await == Some(1) { + // return anyhow::Ok(false); + // } else { + // active_call + // .update(&mut cx, |call, cx| call.hang_up(cx)) + // .await + // .log_err(); + // } + // } + // } + // } + + // Ok(this + // .update(&mut cx, |this, cx| { + // this.save_all_internal(SaveIntent::Close, cx) + // })? + // .await?) + // }) + // } + + // fn save_all( + // &mut self, + // action: &SaveAll, + // cx: &mut ViewContext, + // ) -> Option>> { + // let save_all = + // self.save_all_internal(action.save_intent.unwrap_or(SaveIntent::SaveAll), cx); + // Some(cx.foreground().spawn(async move { + // save_all.await?; + // Ok(()) + // })) + // } + + // fn save_all_internal( + // &mut self, + // mut save_intent: SaveIntent, + // cx: &mut ViewContext, + // ) -> Task> { + // if self.project.read(cx).is_read_only() { + // return Task::ready(Ok(true)); + // } + // let dirty_items = self + // .panes + // .iter() + // .flat_map(|pane| { + // pane.read(cx).items().filter_map(|item| { + // if item.is_dirty(cx) { + // Some((pane.downgrade(), item.boxed_clone())) + // } else { + // None + // } + // }) + // }) + // .collect::>(); + + // let project = self.project.clone(); + // cx.spawn(|workspace, mut cx| async move { + // // Override save mode and display "Save all files" prompt + // if save_intent == SaveIntent::Close && dirty_items.len() > 1 { + // let mut answer = workspace.update(&mut cx, |_, cx| { + // let prompt = Pane::file_names_for_prompt( + // &mut dirty_items.iter().map(|(_, handle)| handle), + // dirty_items.len(), + // cx, + // ); + // cx.prompt( + // PromptLevel::Warning, + // &prompt, + // &["Save all", "Discard all", "Cancel"], + // ) + // })?; + // match answer.next().await { + // Some(0) => save_intent = SaveIntent::SaveAll, + // Some(1) => save_intent = SaveIntent::Skip, + // _ => {} + // } + // } + // for (pane, item) in dirty_items { + // let (singleton, project_entry_ids) = + // cx.read(|cx| (item.is_singleton(cx), item.project_entry_ids(cx))); + // if singleton || !project_entry_ids.is_empty() { + // if let Some(ix) = + // pane.read_with(&cx, |pane, _| pane.index_for_item(item.as_ref()))? + // { + // if !Pane::save_item( + // project.clone(), + // &pane, + // ix, + // &*item, + // save_intent, + // &mut cx, + // ) + // .await? + // { + // return Ok(false); + // } + // } + // } + // } + // Ok(true) + // }) + // } + + // pub fn open(&mut self, _: &Open, cx: &mut ViewContext) -> Option>> { + // let mut paths = cx.prompt_for_paths(PathPromptOptions { + // files: true, + // directories: true, + // multiple: true, + // }); + + // Some(cx.spawn(|this, mut cx| async move { + // if let Some(paths) = paths.recv().await.flatten() { + // if let Some(task) = this + // .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx)) + // .log_err() + // { + // task.await? + // } + // } + // Ok(()) + // })) + // } + + // pub fn open_workspace_for_paths( + // &mut self, + // paths: Vec, + // cx: &mut ViewContext, + // ) -> Task> { + // let window = cx.window().downcast::(); + // let is_remote = self.project.read(cx).is_remote(); + // let has_worktree = self.project.read(cx).worktrees(cx).next().is_some(); + // let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx)); + // let close_task = if is_remote || has_worktree || has_dirty_items { + // None + // } else { + // Some(self.prepare_to_close(false, cx)) + // }; + // let app_state = self.app_state.clone(); + + // cx.spawn(|_, mut cx| async move { + // let window_to_replace = if let Some(close_task) = close_task { + // if !close_task.await? { + // return Ok(()); + // } + // window + // } else { + // None + // }; + // cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx)) + // .await?; + // Ok(()) + // }) + // } + + #[allow(clippy::type_complexity)] + pub fn open_paths( + &mut self, + mut abs_paths: Vec, + visible: bool, + cx: &mut ViewContext, + ) -> Task, anyhow::Error>>>> { + log::info!("open paths {:?}", abs_paths); + + let fs = self.app_state.fs.clone(); + + // Sort the paths to ensure we add worktrees for parents before their children. + abs_paths.sort_unstable(); + cx.spawn(|this, mut cx| async move { + let mut tasks = Vec::with_capacity(abs_paths.len()); + for abs_path in &abs_paths { + let project_path = match this + .update(&mut cx, |this, cx| { + Workspace::project_path_for_path( + this.project.clone(), + abs_path, + visible, + cx, + ) + }) + .log_err() + { + Some(project_path) => project_path.await.log_err(), + None => None, + }; + + let this = this.clone(); + let task = cx.spawn(|mut cx| { + let fs = fs.clone(); + let abs_path = abs_path.clone(); + async move { + let (worktree, project_path) = project_path?; + if fs.is_file(&abs_path).await { + Some( + this.update(&mut cx, |this, cx| { + this.open_path(project_path, None, true, cx) + }) + .log_err()? + .await, + ) + } else { + this.update(&mut cx, |workspace, cx| { + let worktree = worktree.read(cx); + let worktree_abs_path = worktree.abs_path(); + let entry_id = if abs_path == worktree_abs_path.as_ref() { + worktree.root_entry() + } else { + abs_path + .strip_prefix(worktree_abs_path.as_ref()) + .ok() + .and_then(|relative_path| { + worktree.entry_for_path(relative_path) + }) + } + .map(|entry| entry.id); + if let Some(entry_id) = entry_id { + workspace.project.update(cx, |_, cx| { + cx.emit(project2::Event::ActiveEntryChanged(Some( + entry_id, + ))); + }) + } + }) + .log_err()?; + None + } + } + }); + tasks.push(task); + } + + futures::future::join_all(tasks).await + }) + } + + // fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext) { + // let mut paths = cx.prompt_for_paths(PathPromptOptions { + // files: false, + // directories: true, + // multiple: true, + // }); + // cx.spawn(|this, mut cx| async move { + // if let Some(paths) = paths.recv().await.flatten() { + // let results = this + // .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))? + // .await; + // for result in results.into_iter().flatten() { + // result.log_err(); + // } + // } + // anyhow::Ok(()) + // }) + // .detach_and_log_err(cx); + // } + + fn project_path_for_path( + project: Handle, + abs_path: &Path, + visible: bool, + cx: &mut AppContext, + ) -> Task, ProjectPath)>> { + let entry = project.update(cx, |project, cx| { + project.find_or_create_local_worktree(abs_path, visible, cx) + }); + cx.spawn(|cx| async move { + let (worktree, path) = entry.await?; + let worktree_id = worktree.update(&mut cx, |t, _| t.id())?; + Ok(( + worktree, + ProjectPath { + worktree_id, + path: path.into(), + }, + )) + }) + } + + // /// Returns the modal that was toggled closed if it was open. + // pub fn toggle_modal( + // &mut self, + // cx: &mut ViewContext, + // add_view: F, + // ) -> Option> + // where + // V: 'static + Modal, + // F: FnOnce(&mut Self, &mut ViewContext) -> View, + // { + // cx.notify(); + // // Whatever modal was visible is getting clobbered. If its the same type as V, then return + // // it. Otherwise, create a new modal and set it as active. + // if let Some(already_open_modal) = self + // .dismiss_modal(cx) + // .and_then(|modal| modal.downcast::()) + // { + // cx.focus_self(); + // Some(already_open_modal) + // } else { + // let modal = add_view(self, cx); + // cx.subscribe(&modal, |this, _, event, cx| { + // if V::dismiss_on_event(event) { + // this.dismiss_modal(cx); + // } + // }) + // .detach(); + // let previously_focused_view_id = cx.focused_view_id(); + // cx.focus(&modal); + // self.modal = Some(ActiveModal { + // view: Box::new(modal), + // previously_focused_view_id, + // }); + // None + // } + // } + + // pub fn modal(&self) -> Option> { + // self.modal + // .as_ref() + // .and_then(|modal| modal.view.as_any().clone().downcast::()) + // } + + // pub fn dismiss_modal(&mut self, cx: &mut ViewContext) -> Option { + // if let Some(modal) = self.modal.take() { + // if let Some(previously_focused_view_id) = modal.previously_focused_view_id { + // if modal.view.has_focus(cx) { + // cx.window_context().focus(Some(previously_focused_view_id)); + // } + // } + // cx.notify(); + // Some(modal.view.as_any().clone()) + // } else { + // None + // } + // } + + // pub fn items<'a>( + // &'a self, + // cx: &'a AppContext, + // ) -> impl 'a + Iterator> { + // self.panes.iter().flat_map(|pane| pane.read(cx).items()) + // } + + // pub fn item_of_type(&self, cx: &AppContext) -> Option> { + // self.items_of_type(cx).max_by_key(|item| item.id()) + // } + + // pub fn items_of_type<'a, T: Item>( + // &'a self, + // cx: &'a AppContext, + // ) -> impl 'a + Iterator> { + // self.panes + // .iter() + // .flat_map(|pane| pane.read(cx).items_of_type()) + // } + + // pub fn active_item(&self, cx: &AppContext) -> Option> { + // self.active_pane().read(cx).active_item() + // } + + // fn active_project_path(&self, cx: &ViewContext) -> Option { + // self.active_item(cx).and_then(|item| item.project_path(cx)) + // } + + // pub fn save_active_item( + // &mut self, + // save_intent: SaveIntent, + // cx: &mut ViewContext, + // ) -> Task> { + // let project = self.project.clone(); + // let pane = self.active_pane(); + // let item_ix = pane.read(cx).active_item_index(); + // let item = pane.read(cx).active_item(); + // let pane = pane.downgrade(); + + // cx.spawn(|_, mut cx| async move { + // if let Some(item) = item { + // Pane::save_item(project, &pane, item_ix, item.as_ref(), save_intent, &mut cx) + // .await + // .map(|_| ()) + // } else { + // Ok(()) + // } + // }) + // } + + // pub fn close_inactive_items_and_panes( + // &mut self, + // _: &CloseInactiveTabsAndPanes, + // cx: &mut ViewContext, + // ) -> Option>> { + // self.close_all_internal(true, SaveIntent::Close, cx) + // } + + // pub fn close_all_items_and_panes( + // &mut self, + // action: &CloseAllItemsAndPanes, + // cx: &mut ViewContext, + // ) -> Option>> { + // self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx) + // } + + // fn close_all_internal( + // &mut self, + // retain_active_pane: bool, + // save_intent: SaveIntent, + // cx: &mut ViewContext, + // ) -> Option>> { + // let current_pane = self.active_pane(); + + // let mut tasks = Vec::new(); + + // if retain_active_pane { + // if let Some(current_pane_close) = current_pane.update(cx, |pane, cx| { + // pane.close_inactive_items(&CloseInactiveItems, cx) + // }) { + // tasks.push(current_pane_close); + // }; + // } + + // for pane in self.panes() { + // if retain_active_pane && pane.id() == current_pane.id() { + // continue; + // } + + // if let Some(close_pane_items) = pane.update(cx, |pane: &mut Pane, cx| { + // pane.close_all_items( + // &CloseAllItems { + // save_intent: Some(save_intent), + // }, + // cx, + // ) + // }) { + // tasks.push(close_pane_items) + // } + // } + + // if tasks.is_empty() { + // None + // } else { + // Some(cx.spawn(|_, _| async move { + // for task in tasks { + // task.await? + // } + // Ok(()) + // })) + // } + // } + + // pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext) { + // let dock = match dock_side { + // DockPosition::Left => &self.left_dock, + // DockPosition::Bottom => &self.bottom_dock, + // DockPosition::Right => &self.right_dock, + // }; + // let mut focus_center = false; + // let mut reveal_dock = false; + // dock.update(cx, |dock, cx| { + // let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side); + // let was_visible = dock.is_open() && !other_is_zoomed; + // dock.set_open(!was_visible, cx); + + // if let Some(active_panel) = dock.active_panel() { + // if was_visible { + // if active_panel.has_focus(cx) { + // focus_center = true; + // } + // } else { + // cx.focus(active_panel.as_any()); + // reveal_dock = true; + // } + // } + // }); + + // if reveal_dock { + // self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx); + // } + + // if focus_center { + // cx.focus_self(); + // } + + // cx.notify(); + // self.serialize_workspace(cx); + // } + + // pub fn close_all_docks(&mut self, cx: &mut ViewContext) { + // let docks = [&self.left_dock, &self.bottom_dock, &self.right_dock]; + + // for dock in docks { + // dock.update(cx, |dock, cx| { + // dock.set_open(false, cx); + // }); + // } + + // cx.focus_self(); + // cx.notify(); + // self.serialize_workspace(cx); + // } + + // /// Transfer focus to the panel of the given type. + // pub fn focus_panel(&mut self, cx: &mut ViewContext) -> Option> { + // self.focus_or_unfocus_panel::(cx, |_, _| true)? + // .as_any() + // .clone() + // .downcast() + // } + + // /// Focus the panel of the given type if it isn't already focused. If it is + // /// already focused, then transfer focus back to the workspace center. + // pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { + // self.focus_or_unfocus_panel::(cx, |panel, cx| !panel.has_focus(cx)); + // } + + // /// Focus or unfocus the given panel type, depending on the given callback. + // fn focus_or_unfocus_panel( + // &mut self, + // cx: &mut ViewContext, + // should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool, + // ) -> Option> { + // for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { + // if let Some(panel_index) = dock.read(cx).panel_index_for_type::() { + // let mut focus_center = false; + // let mut reveal_dock = false; + // let panel = dock.update(cx, |dock, cx| { + // dock.activate_panel(panel_index, cx); + + // let panel = dock.active_panel().cloned(); + // if let Some(panel) = panel.as_ref() { + // if should_focus(&**panel, cx) { + // dock.set_open(true, cx); + // cx.focus(panel.as_any()); + // reveal_dock = true; + // } else { + // // if panel.is_zoomed(cx) { + // // dock.set_open(false, cx); + // // } + // focus_center = true; + // } + // } + // panel + // }); + + // if focus_center { + // cx.focus_self(); + // } + + // self.serialize_workspace(cx); + // cx.notify(); + // return panel; + // } + // } + // None + // } + + // pub fn panel(&self, cx: &WindowContext) -> Option> { + // for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { + // let dock = dock.read(cx); + // if let Some(panel) = dock.panel::() { + // return Some(panel); + // } + // } + // None + // } + + // fn zoom_out(&mut self, cx: &mut ViewContext) { + // for pane in &self.panes { + // pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + // } + + // self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + // self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + // self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + // self.zoomed = None; + // self.zoomed_position = None; + + // cx.notify(); + // } + + // #[cfg(any(test, feature = "test-support"))] + // pub fn zoomed_view(&self, cx: &AppContext) -> Option { + // self.zoomed.and_then(|view| view.upgrade(cx)) + // } + + // fn dismiss_zoomed_items_to_reveal( + // &mut self, + // dock_to_reveal: Option, + // cx: &mut ViewContext, + // ) { + // // If a center pane is zoomed, unzoom it. + // for pane in &self.panes { + // if pane != &self.active_pane || dock_to_reveal.is_some() { + // pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + // } + // } + + // // If another dock is zoomed, hide it. + // let mut focus_center = false; + // for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] { + // dock.update(cx, |dock, cx| { + // if Some(dock.position()) != dock_to_reveal { + // if let Some(panel) = dock.active_panel() { + // if panel.is_zoomed(cx) { + // focus_center |= panel.has_focus(cx); + // dock.set_open(false, cx); + // } + // } + // } + // }); + // } + + // if focus_center { + // cx.focus_self(); + // } + + // if self.zoomed_position != dock_to_reveal { + // self.zoomed = None; + // self.zoomed_position = None; + // } + + // cx.notify(); + // } + + // fn add_pane(&mut self, cx: &mut ViewContext) -> View { + // let pane = cx.add_view(|cx| { + // Pane::new( + // self.weak_handle(), + // self.project.clone(), + // self.pane_history_timestamp.clone(), + // cx, + // ) + // }); + // cx.subscribe(&pane, Self::handle_pane_event).detach(); + // self.panes.push(pane.clone()); + // cx.focus(&pane); + // cx.emit(Event::PaneAdded(pane.clone())); + // pane + // } + + // pub fn add_item_to_center( + // &mut self, + // item: Box, + // cx: &mut ViewContext, + // ) -> bool { + // if let Some(center_pane) = self.last_active_center_pane.clone() { + // if let Some(center_pane) = center_pane.upgrade(cx) { + // center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx)); + // true + // } else { + // false + // } + // } else { + // false + // } + // } + + // pub fn add_item(&mut self, item: Box, cx: &mut ViewContext) { + // self.active_pane + // .update(cx, |pane, cx| pane.add_item(item, true, true, None, cx)); + // } + + // pub fn split_item( + // &mut self, + // split_direction: SplitDirection, + // item: Box, + // cx: &mut ViewContext, + // ) { + // let new_pane = self.split_pane(self.active_pane.clone(), split_direction, cx); + // new_pane.update(cx, move |new_pane, cx| { + // new_pane.add_item(item, true, true, None, cx) + // }) + // } + + // pub fn open_abs_path( + // &mut self, + // abs_path: PathBuf, + // visible: bool, + // cx: &mut ViewContext, + // ) -> Task>> { + // cx.spawn(|workspace, mut cx| async move { + // let open_paths_task_result = workspace + // .update(&mut cx, |workspace, cx| { + // workspace.open_paths(vec![abs_path.clone()], visible, cx) + // }) + // .with_context(|| format!("open abs path {abs_path:?} task spawn"))? + // .await; + // anyhow::ensure!( + // open_paths_task_result.len() == 1, + // "open abs path {abs_path:?} task returned incorrect number of results" + // ); + // match open_paths_task_result + // .into_iter() + // .next() + // .expect("ensured single task result") + // { + // Some(open_result) => { + // open_result.with_context(|| format!("open abs path {abs_path:?} task join")) + // } + // None => anyhow::bail!("open abs path {abs_path:?} task returned None"), + // } + // }) + // } + + // pub fn split_abs_path( + // &mut self, + // abs_path: PathBuf, + // visible: bool, + // cx: &mut ViewContext, + // ) -> Task>> { + // let project_path_task = + // Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx); + // cx.spawn(|this, mut cx| async move { + // let (_, path) = project_path_task.await?; + // this.update(&mut cx, |this, cx| this.split_path(path, cx))? + // .await + // }) + // } + + pub fn open_path( + &mut self, + path: impl Into, + pane: Option>, + focus_item: bool, + cx: &mut ViewContext, + ) -> Task, anyhow::Error>> { + let pane = pane.unwrap_or_else(|| { + self.last_active_center_pane.clone().unwrap_or_else(|| { + self.panes + .first() + .expect("There must be an active pane") + .downgrade() + }) + }); + + let task = self.load_path(path.into(), cx); + cx.spawn(|_, mut cx| async move { + let (project_entry_id, build_item) = task.await?; + pane.update(&mut cx, |pane, cx| { + pane.open_item(project_entry_id, focus_item, cx, build_item) + }) + }) + } + + // pub fn split_path( + // &mut self, + // path: impl Into, + // cx: &mut ViewContext, + // ) -> Task, anyhow::Error>> { + // let pane = self.last_active_center_pane.clone().unwrap_or_else(|| { + // self.panes + // .first() + // .expect("There must be an active pane") + // .downgrade() + // }); + + // if let Member::Pane(center_pane) = &self.center.root { + // if center_pane.read(cx).items_len() == 0 { + // return self.open_path(path, Some(pane), true, cx); + // } + // } + + // let task = self.load_path(path.into(), cx); + // cx.spawn(|this, mut cx| async move { + // let (project_entry_id, build_item) = task.await?; + // this.update(&mut cx, move |this, cx| -> Option<_> { + // let pane = pane.upgrade(cx)?; + // let new_pane = this.split_pane(pane, SplitDirection::Right, cx); + // new_pane.update(cx, |new_pane, cx| { + // Some(new_pane.open_item(project_entry_id, true, cx, build_item)) + // }) + // }) + // .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))? + // }) + // } + + pub(crate) fn load_path( + &mut self, + path: ProjectPath, + cx: &mut ViewContext, + ) -> Task< + Result<( + ProjectEntryId, + impl 'static + FnOnce(&mut ViewContext) -> Box, + )>, + > { + let project = self.project().clone(); + let project_item = project.update(cx, |project, cx| project.open_path(path, cx)); + cx.spawn(|_, mut cx| async move { + let (project_entry_id, project_item) = project_item.await?; + let build_item = cx.update(|cx| { + cx.default_global::() + .get(&project_item.model_type()) + .ok_or_else(|| anyhow!("no item builder for project item")) + .cloned() + })?; + let build_item = + move |cx: &mut ViewContext| build_item(project, project_item, cx); + Ok((project_entry_id, build_item)) + }) + } + + // pub fn open_project_item( + // &mut self, + // project_item: ModelHandle, + // cx: &mut ViewContext, + // ) -> View + // where + // T: ProjectItem, + // { + // use project::Item as _; + + // let entry_id = project_item.read(cx).entry_id(cx); + // if let Some(item) = entry_id + // .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx)) + // .and_then(|item| item.downcast()) + // { + // self.activate_item(&item, cx); + // return item; + // } + + // let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); + // self.add_item(Box::new(item.clone()), cx); + // item + // } + + // pub fn split_project_item( + // &mut self, + // project_item: ModelHandle, + // cx: &mut ViewContext, + // ) -> View + // where + // T: ProjectItem, + // { + // use project::Item as _; + + // let entry_id = project_item.read(cx).entry_id(cx); + // if let Some(item) = entry_id + // .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx)) + // .and_then(|item| item.downcast()) + // { + // self.activate_item(&item, cx); + // return item; + // } + + // let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); + // self.split_item(SplitDirection::Right, Box::new(item.clone()), cx); + // item + // } + + // pub fn open_shared_screen(&mut self, peer_id: PeerId, cx: &mut ViewContext) { + // if let Some(shared_screen) = self.shared_screen_for_peer(peer_id, &self.active_pane, cx) { + // self.active_pane.update(cx, |pane, cx| { + // pane.add_item(Box::new(shared_screen), false, true, None, cx) + // }); + // } + // } + + // pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext) -> bool { + // let result = self.panes.iter().find_map(|pane| { + // pane.read(cx) + // .index_for_item(item) + // .map(|ix| (pane.clone(), ix)) + // }); + // if let Some((pane, ix)) = result { + // pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, cx)); + // true + // } else { + // false + // } + // } + + // fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext) { + // let panes = self.center.panes(); + // if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { + // cx.focus(&pane); + // } else { + // self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, cx); + // } + // } + + // pub fn activate_next_pane(&mut self, cx: &mut ViewContext) { + // let panes = self.center.panes(); + // if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { + // let next_ix = (ix + 1) % panes.len(); + // let next_pane = panes[next_ix].clone(); + // cx.focus(&next_pane); + // } + // } + + // pub fn activate_previous_pane(&mut self, cx: &mut ViewContext) { + // let panes = self.center.panes(); + // if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { + // let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1); + // let prev_pane = panes[prev_ix].clone(); + // cx.focus(&prev_pane); + // } + // } + + // pub fn activate_pane_in_direction( + // &mut self, + // direction: SplitDirection, + // cx: &mut ViewContext, + // ) { + // if let Some(pane) = self.find_pane_in_direction(direction, cx) { + // cx.focus(pane); + // } + // } + + // pub fn swap_pane_in_direction( + // &mut self, + // direction: SplitDirection, + // cx: &mut ViewContext, + // ) { + // if let Some(to) = self + // .find_pane_in_direction(direction, cx) + // .map(|pane| pane.clone()) + // { + // self.center.swap(&self.active_pane.clone(), &to); + // cx.notify(); + // } + // } + + // fn find_pane_in_direction( + // &mut self, + // direction: SplitDirection, + // cx: &mut ViewContext, + // ) -> Option<&View> { + // let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else { + // return None; + // }; + // let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx); + // let center = match cursor { + // Some(cursor) if bounding_box.contains_point(cursor) => cursor, + // _ => bounding_box.center(), + // }; + + // let distance_to_next = theme::current(cx).workspace.pane_divider.width + 1.; + + // let target = match direction { + // SplitDirection::Left => vec2f(bounding_box.origin_x() - distance_to_next, center.y()), + // SplitDirection::Right => vec2f(bounding_box.max_x() + distance_to_next, center.y()), + // SplitDirection::Up => vec2f(center.x(), bounding_box.origin_y() - distance_to_next), + // SplitDirection::Down => vec2f(center.x(), bounding_box.max_y() + distance_to_next), + // }; + // self.center.pane_at_pixel_position(target) + // } + + // fn handle_pane_focused(&mut self, pane: View, cx: &mut ViewContext) { + // if self.active_pane != pane { + // self.active_pane = pane.clone(); + // self.status_bar.update(cx, |status_bar, cx| { + // status_bar.set_active_pane(&self.active_pane, cx); + // }); + // self.active_item_path_changed(cx); + // self.last_active_center_pane = Some(pane.downgrade()); + // } + + // self.dismiss_zoomed_items_to_reveal(None, cx); + // if pane.read(cx).is_zoomed() { + // self.zoomed = Some(pane.downgrade().into_any()); + // } else { + // self.zoomed = None; + // } + // self.zoomed_position = None; + // self.update_active_view_for_followers(cx); + + // cx.notify(); + // } + + // fn handle_pane_event( + // &mut self, + // pane: View, + // event: &pane::Event, + // cx: &mut ViewContext, + // ) { + // match event { + // pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx), + // pane::Event::Split(direction) => { + // self.split_and_clone(pane, *direction, cx); + // } + // pane::Event::Remove => self.remove_pane(pane, cx), + // pane::Event::ActivateItem { local } => { + // if *local { + // self.unfollow(&pane, cx); + // } + // if &pane == self.active_pane() { + // self.active_item_path_changed(cx); + // } + // } + // pane::Event::ChangeItemTitle => { + // if pane == self.active_pane { + // self.active_item_path_changed(cx); + // } + // self.update_window_edited(cx); + // } + // pane::Event::RemoveItem { item_id } => { + // self.update_window_edited(cx); + // if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) { + // if entry.get().id() == pane.id() { + // entry.remove(); + // } + // } + // } + // pane::Event::Focus => { + // self.handle_pane_focused(pane.clone(), cx); + // } + // pane::Event::ZoomIn => { + // if pane == self.active_pane { + // pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); + // if pane.read(cx).has_focus() { + // self.zoomed = Some(pane.downgrade().into_any()); + // self.zoomed_position = None; + // } + // cx.notify(); + // } + // } + // pane::Event::ZoomOut => { + // pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + // if self.zoomed_position.is_none() { + // self.zoomed = None; + // } + // cx.notify(); + // } + // } + + // self.serialize_workspace(cx); + // } + + // pub fn split_pane( + // &mut self, + // pane_to_split: View, + // split_direction: SplitDirection, + // cx: &mut ViewContext, + // ) -> View { + // let new_pane = self.add_pane(cx); + // self.center + // .split(&pane_to_split, &new_pane, split_direction) + // .unwrap(); + // cx.notify(); + // new_pane + // } + + // pub fn split_and_clone( + // &mut self, + // pane: View, + // direction: SplitDirection, + // cx: &mut ViewContext, + // ) -> Option> { + // let item = pane.read(cx).active_item()?; + // let maybe_pane_handle = if let Some(clone) = item.clone_on_split(self.database_id(), cx) { + // let new_pane = self.add_pane(cx); + // new_pane.update(cx, |pane, cx| pane.add_item(clone, true, true, None, cx)); + // self.center.split(&pane, &new_pane, direction).unwrap(); + // Some(new_pane) + // } else { + // None + // }; + // cx.notify(); + // maybe_pane_handle + // } + + // pub fn split_pane_with_item( + // &mut self, + // pane_to_split: WeakViewHandle, + // split_direction: SplitDirection, + // from: WeakViewHandle, + // item_id_to_move: usize, + // cx: &mut ViewContext, + // ) { + // let Some(pane_to_split) = pane_to_split.upgrade(cx) else { + // return; + // }; + // let Some(from) = from.upgrade(cx) else { + // return; + // }; + + // let new_pane = self.add_pane(cx); + // self.move_item(from.clone(), new_pane.clone(), item_id_to_move, 0, cx); + // self.center + // .split(&pane_to_split, &new_pane, split_direction) + // .unwrap(); + // cx.notify(); + // } + + // pub fn split_pane_with_project_entry( + // &mut self, + // pane_to_split: WeakViewHandle, + // split_direction: SplitDirection, + // project_entry: ProjectEntryId, + // cx: &mut ViewContext, + // ) -> Option>> { + // let pane_to_split = pane_to_split.upgrade(cx)?; + // let new_pane = self.add_pane(cx); + // self.center + // .split(&pane_to_split, &new_pane, split_direction) + // .unwrap(); + + // let path = self.project.read(cx).path_for_entry(project_entry, cx)?; + // let task = self.open_path(path, Some(new_pane.downgrade()), true, cx); + // Some(cx.foreground().spawn(async move { + // task.await?; + // Ok(()) + // })) + // } + + // pub fn move_item( + // &mut self, + // source: View, + // destination: View, + // item_id_to_move: usize, + // destination_index: usize, + // cx: &mut ViewContext, + // ) { + // let item_to_move = source + // .read(cx) + // .items() + // .enumerate() + // .find(|(_, item_handle)| item_handle.id() == item_id_to_move); + + // if item_to_move.is_none() { + // log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop"); + // return; + // } + // let (item_ix, item_handle) = item_to_move.unwrap(); + // let item_handle = item_handle.clone(); + + // if source != destination { + // // Close item from previous pane + // source.update(cx, |source, cx| { + // source.remove_item(item_ix, false, cx); + // }); + // } + + // // This automatically removes duplicate items in the pane + // destination.update(cx, |destination, cx| { + // destination.add_item(item_handle, true, true, Some(destination_index), cx); + // cx.focus_self(); + // }); + // } + + // fn remove_pane(&mut self, pane: View, cx: &mut ViewContext) { + // if self.center.remove(&pane).unwrap() { + // self.force_remove_pane(&pane, cx); + // self.unfollow(&pane, cx); + // self.last_leaders_by_pane.remove(&pane.downgrade()); + // for removed_item in pane.read(cx).items() { + // self.panes_by_item.remove(&removed_item.id()); + // } + + // cx.notify(); + // } else { + // self.active_item_path_changed(cx); + // } + // } + + // pub fn panes(&self) -> &[View] { + // &self.panes + // } + + // pub fn active_pane(&self) -> &View { + // &self.active_pane + // } + + // fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext) { + // self.follower_states.retain(|_, state| { + // if state.leader_id == peer_id { + // for item in state.items_by_leader_view_id.values() { + // item.set_leader_peer_id(None, cx); + // } + // false + // } else { + // true + // } + // }); + // cx.notify(); + // } + + // fn start_following( + // &mut self, + // leader_id: PeerId, + // cx: &mut ViewContext, + // ) -> Option>> { + // let pane = self.active_pane().clone(); + + // self.last_leaders_by_pane + // .insert(pane.downgrade(), leader_id); + // self.unfollow(&pane, cx); + // self.follower_states.insert( + // pane.clone(), + // FollowerState { + // leader_id, + // active_view_id: None, + // items_by_leader_view_id: Default::default(), + // }, + // ); + // cx.notify(); + + // let room_id = self.active_call()?.read(cx).room()?.read(cx).id(); + // let project_id = self.project.read(cx).remote_id(); + // let request = self.app_state.client.request(proto::Follow { + // room_id, + // project_id, + // leader_id: Some(leader_id), + // }); + + // Some(cx.spawn(|this, mut cx| async move { + // let response = request.await?; + // this.update(&mut cx, |this, _| { + // let state = this + // .follower_states + // .get_mut(&pane) + // .ok_or_else(|| anyhow!("following interrupted"))?; + // state.active_view_id = if let Some(active_view_id) = response.active_view_id { + // Some(ViewId::from_proto(active_view_id)?) + // } else { + // None + // }; + // Ok::<_, anyhow::Error>(()) + // })??; + // Self::add_views_from_leader( + // this.clone(), + // leader_id, + // vec![pane], + // response.views, + // &mut cx, + // ) + // .await?; + // this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?; + // Ok(()) + // })) + // } + + // pub fn follow_next_collaborator( + // &mut self, + // _: &FollowNextCollaborator, + // cx: &mut ViewContext, + // ) -> Option>> { + // let collaborators = self.project.read(cx).collaborators(); + // let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) { + // let mut collaborators = collaborators.keys().copied(); + // for peer_id in collaborators.by_ref() { + // if peer_id == leader_id { + // break; + // } + // } + // collaborators.next() + // } else if let Some(last_leader_id) = + // self.last_leaders_by_pane.get(&self.active_pane.downgrade()) + // { + // if collaborators.contains_key(last_leader_id) { + // Some(*last_leader_id) + // } else { + // None + // } + // } else { + // None + // }; + + // let pane = self.active_pane.clone(); + // let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next()) + // else { + // return None; + // }; + // if Some(leader_id) == self.unfollow(&pane, cx) { + // return None; + // } + // self.follow(leader_id, cx) + // } + + // pub fn follow( + // &mut self, + // leader_id: PeerId, + // cx: &mut ViewContext, + // ) -> Option>> { + // let room = ActiveCall::global(cx).read(cx).room()?.read(cx); + // let project = self.project.read(cx); + + // let Some(remote_participant) = room.remote_participant_for_peer_id(leader_id) else { + // return None; + // }; + + // let other_project_id = match remote_participant.location { + // call::ParticipantLocation::External => None, + // call::ParticipantLocation::UnsharedProject => None, + // call::ParticipantLocation::SharedProject { project_id } => { + // if Some(project_id) == project.remote_id() { + // None + // } else { + // Some(project_id) + // } + // } + // }; + + // // if they are active in another project, follow there. + // if let Some(project_id) = other_project_id { + // let app_state = self.app_state.clone(); + // return Some(crate::join_remote_project( + // project_id, + // remote_participant.user.id, + // app_state, + // cx, + // )); + // } + + // // if you're already following, find the right pane and focus it. + // for (pane, state) in &self.follower_states { + // if leader_id == state.leader_id { + // cx.focus(pane); + // return None; + // } + // } + + // // Otherwise, follow. + // self.start_following(leader_id, cx) + // } + + // pub fn unfollow( + // &mut self, + // pane: &View, + // cx: &mut ViewContext, + // ) -> Option { + // let state = self.follower_states.remove(pane)?; + // let leader_id = state.leader_id; + // for (_, item) in state.items_by_leader_view_id { + // item.set_leader_peer_id(None, cx); + // } + + // if self + // .follower_states + // .values() + // .all(|state| state.leader_id != state.leader_id) + // { + // let project_id = self.project.read(cx).remote_id(); + // let room_id = self.active_call()?.read(cx).room()?.read(cx).id(); + // self.app_state + // .client + // .send(proto::Unfollow { + // room_id, + // project_id, + // leader_id: Some(leader_id), + // }) + // .log_err(); + // } + + // cx.notify(); + // Some(leader_id) + // } + + // pub fn is_being_followed(&self, peer_id: PeerId) -> bool { + // self.follower_states + // .values() + // .any(|state| state.leader_id == peer_id) + // } + + // fn render_titlebar(&self, theme: &Theme, cx: &mut ViewContext) -> AnyElement { + // // TODO: There should be a better system in place for this + // // (https://github.com/zed-industries/zed/issues/1290) + // let is_fullscreen = cx.window_is_fullscreen(); + // let container_theme = if is_fullscreen { + // let mut container_theme = theme.titlebar.container; + // container_theme.padding.left = container_theme.padding.right; + // container_theme + // } else { + // theme.titlebar.container + // }; + + // enum TitleBar {} + // MouseEventHandler::new::(0, cx, |_, cx| { + // Stack::new() + // .with_children( + // self.titlebar_item + // .as_ref() + // .map(|item| ChildView::new(item, cx)), + // ) + // .contained() + // .with_style(container_theme) + // }) + // .on_click(MouseButton::Left, |event, _, cx| { + // if event.click_count == 2 { + // cx.zoom_window(); + // } + // }) + // .constrained() + // .with_height(theme.titlebar.height) + // .into_any_named("titlebar") + // } + + // fn active_item_path_changed(&mut self, cx: &mut ViewContext) { + // let active_entry = self.active_project_path(cx); + // self.project + // .update(cx, |project, cx| project.set_active_path(active_entry, cx)); + // self.update_window_title(cx); + // } + + // fn update_window_title(&mut self, cx: &mut ViewContext) { + // let project = self.project().read(cx); + // let mut title = String::new(); + + // if let Some(path) = self.active_item(cx).and_then(|item| item.project_path(cx)) { + // let filename = path + // .path + // .file_name() + // .map(|s| s.to_string_lossy()) + // .or_else(|| { + // Some(Cow::Borrowed( + // project + // .worktree_for_id(path.worktree_id, cx)? + // .read(cx) + // .root_name(), + // )) + // }); + + // if let Some(filename) = filename { + // title.push_str(filename.as_ref()); + // title.push_str(" — "); + // } + // } + + // for (i, name) in project.worktree_root_names(cx).enumerate() { + // if i > 0 { + // title.push_str(", "); + // } + // title.push_str(name); + // } + + // if title.is_empty() { + // title = "empty project".to_string(); + // } + + // if project.is_remote() { + // title.push_str(" ↙"); + // } else if project.is_shared() { + // title.push_str(" ↗"); + // } + + // cx.set_window_title(&title); + // } + + // fn update_window_edited(&mut self, cx: &mut ViewContext) { + // let is_edited = !self.project.read(cx).is_read_only() + // && self + // .items(cx) + // .any(|item| item.has_conflict(cx) || item.is_dirty(cx)); + // if is_edited != self.window_edited { + // self.window_edited = is_edited; + // cx.set_window_edited(self.window_edited) + // } + // } + + // fn render_disconnected_overlay( + // &self, + // cx: &mut ViewContext, + // ) -> Option> { + // if self.project.read(cx).is_read_only() { + // enum DisconnectedOverlay {} + // Some( + // MouseEventHandler::new::(0, cx, |_, cx| { + // let theme = &theme::current(cx); + // Label::new( + // "Your connection to the remote project has been lost.", + // theme.workspace.disconnected_overlay.text.clone(), + // ) + // .aligned() + // .contained() + // .with_style(theme.workspace.disconnected_overlay.container) + // }) + // .with_cursor_style(CursorStyle::Arrow) + // .capture_all() + // .into_any_named("disconnected overlay"), + // ) + // } else { + // None + // } + // } + + // fn render_notifications( + // &self, + // theme: &theme::Workspace, + // cx: &AppContext, + // ) -> Option> { + // if self.notifications.is_empty() { + // None + // } else { + // Some( + // Flex::column() + // .with_children(self.notifications.iter().map(|(_, _, notification)| { + // ChildView::new(notification.as_any(), cx) + // .contained() + // .with_style(theme.notification) + // })) + // .constrained() + // .with_width(theme.notifications.width) + // .contained() + // .with_style(theme.notifications.container) + // .aligned() + // .bottom() + // .right() + // .into_any(), + // ) + // } + // } + + // // RPC handlers + + // fn handle_follow( + // &mut self, + // follower_project_id: Option, + // cx: &mut ViewContext, + // ) -> proto::FollowResponse { + // let client = &self.app_state.client; + // let project_id = self.project.read(cx).remote_id(); + + // let active_view_id = self.active_item(cx).and_then(|i| { + // Some( + // i.to_followable_item_handle(cx)? + // .remote_id(client, cx)? + // .to_proto(), + // ) + // }); + + // cx.notify(); + + // self.last_active_view_id = active_view_id.clone(); + // proto::FollowResponse { + // active_view_id, + // views: self + // .panes() + // .iter() + // .flat_map(|pane| { + // let leader_id = self.leader_for_pane(pane); + // pane.read(cx).items().filter_map({ + // let cx = &cx; + // move |item| { + // let item = item.to_followable_item_handle(cx)?; + // if (project_id.is_none() || project_id != follower_project_id) + // && item.is_project_item(cx) + // { + // return None; + // } + // let id = item.remote_id(client, cx)?.to_proto(); + // let variant = item.to_state_proto(cx)?; + // Some(proto::View { + // id: Some(id), + // leader_id, + // variant: Some(variant), + // }) + // } + // }) + // }) + // .collect(), + // } + // } + + // fn handle_update_followers( + // &mut self, + // leader_id: PeerId, + // message: proto::UpdateFollowers, + // _cx: &mut ViewContext, + // ) { + // self.leader_updates_tx + // .unbounded_send((leader_id, message)) + // .ok(); + // } + + // async fn process_leader_update( + // this: &WeakViewHandle, + // leader_id: PeerId, + // update: proto::UpdateFollowers, + // cx: &mut AsyncAppContext, + // ) -> Result<()> { + // match update.variant.ok_or_else(|| anyhow!("invalid update"))? { + // proto::update_followers::Variant::UpdateActiveView(update_active_view) => { + // this.update(cx, |this, _| { + // for (_, state) in &mut this.follower_states { + // if state.leader_id == leader_id { + // state.active_view_id = + // if let Some(active_view_id) = update_active_view.id.clone() { + // Some(ViewId::from_proto(active_view_id)?) + // } else { + // None + // }; + // } + // } + // anyhow::Ok(()) + // })??; + // } + // proto::update_followers::Variant::UpdateView(update_view) => { + // let variant = update_view + // .variant + // .ok_or_else(|| anyhow!("missing update view variant"))?; + // let id = update_view + // .id + // .ok_or_else(|| anyhow!("missing update view id"))?; + // let mut tasks = Vec::new(); + // this.update(cx, |this, cx| { + // let project = this.project.clone(); + // for (_, state) in &mut this.follower_states { + // if state.leader_id == leader_id { + // let view_id = ViewId::from_proto(id.clone())?; + // if let Some(item) = state.items_by_leader_view_id.get(&view_id) { + // tasks.push(item.apply_update_proto(&project, variant.clone(), cx)); + // } + // } + // } + // anyhow::Ok(()) + // })??; + // try_join_all(tasks).await.log_err(); + // } + // proto::update_followers::Variant::CreateView(view) => { + // let panes = this.read_with(cx, |this, _| { + // this.follower_states + // .iter() + // .filter_map(|(pane, state)| (state.leader_id == leader_id).then_some(pane)) + // .cloned() + // .collect() + // })?; + // Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?; + // } + // } + // this.update(cx, |this, cx| this.leader_updated(leader_id, cx))?; + // Ok(()) + // } + + // async fn add_views_from_leader( + // this: WeakViewHandle, + // leader_id: PeerId, + // panes: Vec>, + // views: Vec, + // cx: &mut AsyncAppContext, + // ) -> Result<()> { + // let this = this + // .upgrade(cx) + // .ok_or_else(|| anyhow!("workspace dropped"))?; + + // let item_builders = cx.update(|cx| { + // cx.default_global::() + // .values() + // .map(|b| b.0) + // .collect::>() + // }); + + // let mut item_tasks_by_pane = HashMap::default(); + // for pane in panes { + // let mut item_tasks = Vec::new(); + // let mut leader_view_ids = Vec::new(); + // for view in &views { + // let Some(id) = &view.id else { continue }; + // let id = ViewId::from_proto(id.clone())?; + // let mut variant = view.variant.clone(); + // if variant.is_none() { + // Err(anyhow!("missing view variant"))?; + // } + // for build_item in &item_builders { + // let task = cx + // .update(|cx| build_item(pane.clone(), this.clone(), id, &mut variant, cx)); + // if let Some(task) = task { + // item_tasks.push(task); + // leader_view_ids.push(id); + // break; + // } else { + // assert!(variant.is_some()); + // } + // } + // } + + // item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids)); + // } + + // for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane { + // let items = futures::future::try_join_all(item_tasks).await?; + // this.update(cx, |this, cx| { + // let state = this.follower_states.get_mut(&pane)?; + // for (id, item) in leader_view_ids.into_iter().zip(items) { + // item.set_leader_peer_id(Some(leader_id), cx); + // state.items_by_leader_view_id.insert(id, item); + // } + + // Some(()) + // }); + // } + // Ok(()) + // } + + // fn update_active_view_for_followers(&mut self, cx: &AppContext) { + // let mut is_project_item = true; + // let mut update = proto::UpdateActiveView::default(); + // if self.active_pane.read(cx).has_focus() { + // let item = self + // .active_item(cx) + // .and_then(|item| item.to_followable_item_handle(cx)); + // if let Some(item) = item { + // is_project_item = item.is_project_item(cx); + // update = proto::UpdateActiveView { + // id: item + // .remote_id(&self.app_state.client, cx) + // .map(|id| id.to_proto()), + // leader_id: self.leader_for_pane(&self.active_pane), + // }; + // } + // } + + // if update.id != self.last_active_view_id { + // self.last_active_view_id = update.id.clone(); + // self.update_followers( + // is_project_item, + // proto::update_followers::Variant::UpdateActiveView(update), + // cx, + // ); + // } + // } + + // fn update_followers( + // &self, + // project_only: bool, + // update: proto::update_followers::Variant, + // cx: &AppContext, + // ) -> Option<()> { + // let project_id = if project_only { + // self.project.read(cx).remote_id() + // } else { + // None + // }; + // self.app_state().workspace_store.read_with(cx, |store, cx| { + // store.update_followers(project_id, update, cx) + // }) + // } + + // pub fn leader_for_pane(&self, pane: &View) -> Option { + // self.follower_states.get(pane).map(|state| state.leader_id) + // } + + // fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext) -> Option<()> { + // cx.notify(); + + // let call = self.active_call()?; + // let room = call.read(cx).room()?.read(cx); + // let participant = room.remote_participant_for_peer_id(leader_id)?; + // let mut items_to_activate = Vec::new(); + + // let leader_in_this_app; + // let leader_in_this_project; + // match participant.location { + // call::ParticipantLocation::SharedProject { project_id } => { + // leader_in_this_app = true; + // leader_in_this_project = Some(project_id) == self.project.read(cx).remote_id(); + // } + // call::ParticipantLocation::UnsharedProject => { + // leader_in_this_app = true; + // leader_in_this_project = false; + // } + // call::ParticipantLocation::External => { + // leader_in_this_app = false; + // leader_in_this_project = false; + // } + // }; + + // for (pane, state) in &self.follower_states { + // if state.leader_id != leader_id { + // continue; + // } + // if let (Some(active_view_id), true) = (state.active_view_id, leader_in_this_app) { + // if let Some(item) = state.items_by_leader_view_id.get(&active_view_id) { + // if leader_in_this_project || !item.is_project_item(cx) { + // items_to_activate.push((pane.clone(), item.boxed_clone())); + // } + // } else { + // log::warn!( + // "unknown view id {:?} for leader {:?}", + // active_view_id, + // leader_id + // ); + // } + // continue; + // } + // if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) { + // items_to_activate.push((pane.clone(), Box::new(shared_screen))); + // } + // } + + // for (pane, item) in items_to_activate { + // let pane_was_focused = pane.read(cx).has_focus(); + // if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) { + // pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx)); + // } else { + // pane.update(cx, |pane, cx| { + // pane.add_item(item.boxed_clone(), false, false, None, cx) + // }); + // } + + // if pane_was_focused { + // pane.update(cx, |pane, cx| pane.focus_active_item(cx)); + // } + // } + + // None + // } + + // fn shared_screen_for_peer( + // &self, + // peer_id: PeerId, + // pane: &View, + // cx: &mut ViewContext, + // ) -> Option> { + // let call = self.active_call()?; + // let room = call.read(cx).room()?.read(cx); + // let participant = room.remote_participant_for_peer_id(peer_id)?; + // let track = participant.video_tracks.values().next()?.clone(); + // let user = participant.user.clone(); + + // for item in pane.read(cx).items_of_type::() { + // if item.read(cx).peer_id == peer_id { + // return Some(item); + // } + // } + + // Some(cx.add_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx))) + // } + + // pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { + // if active { + // self.update_active_view_for_followers(cx); + // cx.background() + // .spawn(persistence::DB.update_timestamp(self.database_id())) + // .detach(); + // } else { + // for pane in &self.panes { + // pane.update(cx, |pane, cx| { + // if let Some(item) = pane.active_item() { + // item.workspace_deactivated(cx); + // } + // if matches!( + // settings::get::(cx).autosave, + // AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange + // ) { + // for item in pane.items() { + // Pane::autosave_item(item.as_ref(), self.project.clone(), cx) + // .detach_and_log_err(cx); + // } + // } + // }); + // } + // } + // } + + // fn active_call(&self) -> Option<&ModelHandle> { + // self.active_call.as_ref().map(|(call, _)| call) + // } + + // fn on_active_call_event( + // &mut self, + // _: ModelHandle, + // event: &call::room::Event, + // cx: &mut ViewContext, + // ) { + // match event { + // call::room::Event::ParticipantLocationChanged { participant_id } + // | call::room::Event::RemoteVideoTracksChanged { participant_id } => { + // self.leader_updated(*participant_id, cx); + // } + // _ => {} + // } + // } + + // pub fn database_id(&self) -> WorkspaceId { + // self.database_id + // } + + // fn location(&self, cx: &AppContext) -> Option { + // let project = self.project().read(cx); + + // if project.is_local() { + // Some( + // project + // .visible_worktrees(cx) + // .map(|worktree| worktree.read(cx).abs_path()) + // .collect::>() + // .into(), + // ) + // } else { + // None + // } + // } + + // fn remove_panes(&mut self, member: Member, cx: &mut ViewContext) { + // match member { + // Member::Axis(PaneAxis { members, .. }) => { + // for child in members.iter() { + // self.remove_panes(child.clone(), cx) + // } + // } + // Member::Pane(pane) => { + // self.force_remove_pane(&pane, cx); + // } + // } + // } + + // fn force_remove_pane(&mut self, pane: &View, cx: &mut ViewContext) { + // self.panes.retain(|p| p != pane); + // cx.focus(self.panes.last().unwrap()); + // if self.last_active_center_pane == Some(pane.downgrade()) { + // self.last_active_center_pane = None; + // } + // cx.notify(); + // } + + // fn schedule_serialize(&mut self, cx: &mut ViewContext) { + // self._schedule_serialize = Some(cx.spawn(|this, cx| async move { + // cx.background().timer(Duration::from_millis(100)).await; + // this.read_with(&cx, |this, cx| this.serialize_workspace(cx)) + // .ok(); + // })); + // } + + // fn serialize_workspace(&self, cx: &ViewContext) { + // fn serialize_pane_handle( + // pane_handle: &View, + // cx: &AppContext, + // ) -> SerializedPane { + // let (items, active) = { + // let pane = pane_handle.read(cx); + // let active_item_id = pane.active_item().map(|item| item.id()); + // ( + // pane.items() + // .filter_map(|item_handle| { + // Some(SerializedItem { + // kind: Arc::from(item_handle.serialized_item_kind()?), + // item_id: item_handle.id(), + // active: Some(item_handle.id()) == active_item_id, + // }) + // }) + // .collect::>(), + // pane.has_focus(), + // ) + // }; + + // SerializedPane::new(items, active) + // } + + // fn build_serialized_pane_group( + // pane_group: &Member, + // cx: &AppContext, + // ) -> SerializedPaneGroup { + // match pane_group { + // Member::Axis(PaneAxis { + // axis, + // members, + // flexes, + // bounding_boxes: _, + // }) => SerializedPaneGroup::Group { + // axis: *axis, + // children: members + // .iter() + // .map(|member| build_serialized_pane_group(member, cx)) + // .collect::>(), + // flexes: Some(flexes.borrow().clone()), + // }, + // Member::Pane(pane_handle) => { + // SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx)) + // } + // } + // } + + // fn build_serialized_docks(this: &Workspace, cx: &ViewContext) -> DockStructure { + // let left_dock = this.left_dock.read(cx); + // let left_visible = left_dock.is_open(); + // let left_active_panel = left_dock.visible_panel().and_then(|panel| { + // Some( + // cx.view_ui_name(panel.as_any().window(), panel.id())? + // .to_string(), + // ) + // }); + // let left_dock_zoom = left_dock + // .visible_panel() + // .map(|panel| panel.is_zoomed(cx)) + // .unwrap_or(false); + + // let right_dock = this.right_dock.read(cx); + // let right_visible = right_dock.is_open(); + // let right_active_panel = right_dock.visible_panel().and_then(|panel| { + // Some( + // cx.view_ui_name(panel.as_any().window(), panel.id())? + // .to_string(), + // ) + // }); + // let right_dock_zoom = right_dock + // .visible_panel() + // .map(|panel| panel.is_zoomed(cx)) + // .unwrap_or(false); + + // let bottom_dock = this.bottom_dock.read(cx); + // let bottom_visible = bottom_dock.is_open(); + // let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| { + // Some( + // cx.view_ui_name(panel.as_any().window(), panel.id())? + // .to_string(), + // ) + // }); + // let bottom_dock_zoom = bottom_dock + // .visible_panel() + // .map(|panel| panel.is_zoomed(cx)) + // .unwrap_or(false); + + // DockStructure { + // left: DockData { + // visible: left_visible, + // active_panel: left_active_panel, + // zoom: left_dock_zoom, + // }, + // right: DockData { + // visible: right_visible, + // active_panel: right_active_panel, + // zoom: right_dock_zoom, + // }, + // bottom: DockData { + // visible: bottom_visible, + // active_panel: bottom_active_panel, + // zoom: bottom_dock_zoom, + // }, + // } + // } + + // if let Some(location) = self.location(cx) { + // // Load bearing special case: + // // - with_local_workspace() relies on this to not have other stuff open + // // when you open your log + // if !location.paths().is_empty() { + // let center_group = build_serialized_pane_group(&self.center.root, cx); + // let docks = build_serialized_docks(self, cx); + + // let serialized_workspace = SerializedWorkspace { + // id: self.database_id, + // location, + // center_group, + // bounds: Default::default(), + // display: Default::default(), + // docks, + // }; + + // cx.background() + // .spawn(persistence::DB.save_workspace(serialized_workspace)) + // .detach(); + // } + // } + // } + + // pub(crate) fn load_workspace( + // workspace: WeakViewHandle, + // serialized_workspace: SerializedWorkspace, + // paths_to_open: Vec>, + // cx: &mut AppContext, + // ) -> Task>>>> { + // cx.spawn(|mut cx| async move { + // let (project, old_center_pane) = workspace.read_with(&cx, |workspace, _| { + // ( + // workspace.project().clone(), + // workspace.last_active_center_pane.clone(), + // ) + // })?; + + // let mut center_group = None; + // let mut center_items = None; + // // Traverse the splits tree and add to things + // if let Some((group, active_pane, items)) = serialized_workspace + // .center_group + // .deserialize(&project, serialized_workspace.id, &workspace, &mut cx) + // .await + // { + // center_items = Some(items); + // center_group = Some((group, active_pane)) + // } + + // let mut items_by_project_path = cx.read(|cx| { + // center_items + // .unwrap_or_default() + // .into_iter() + // .filter_map(|item| { + // let item = item?; + // let project_path = item.project_path(cx)?; + // Some((project_path, item)) + // }) + // .collect::>() + // }); + + // let opened_items = paths_to_open + // .into_iter() + // .map(|path_to_open| { + // path_to_open + // .and_then(|path_to_open| items_by_project_path.remove(&path_to_open)) + // }) + // .collect::>(); + + // // Remove old panes from workspace panes list + // workspace.update(&mut cx, |workspace, cx| { + // if let Some((center_group, active_pane)) = center_group { + // workspace.remove_panes(workspace.center.root.clone(), cx); + + // // Swap workspace center group + // workspace.center = PaneGroup::with_root(center_group); + + // // Change the focus to the workspace first so that we retrigger focus in on the pane. + // cx.focus_self(); + + // if let Some(active_pane) = active_pane { + // cx.focus(&active_pane); + // } else { + // cx.focus(workspace.panes.last().unwrap()); + // } + // } else { + // let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade(cx)); + // if let Some(old_center_handle) = old_center_handle { + // cx.focus(&old_center_handle) + // } else { + // cx.focus_self() + // } + // } + + // let docks = serialized_workspace.docks; + // workspace.left_dock.update(cx, |dock, cx| { + // dock.set_open(docks.left.visible, cx); + // if let Some(active_panel) = docks.left.active_panel { + // if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { + // dock.activate_panel(ix, cx); + // } + // } + // dock.active_panel() + // .map(|panel| panel.set_zoomed(docks.left.zoom, cx)); + // if docks.left.visible && docks.left.zoom { + // cx.focus_self() + // } + // }); + // // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something + // workspace.right_dock.update(cx, |dock, cx| { + // dock.set_open(docks.right.visible, cx); + // if let Some(active_panel) = docks.right.active_panel { + // if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { + // dock.activate_panel(ix, cx); + // } + // } + // dock.active_panel() + // .map(|panel| panel.set_zoomed(docks.right.zoom, cx)); + + // if docks.right.visible && docks.right.zoom { + // cx.focus_self() + // } + // }); + // workspace.bottom_dock.update(cx, |dock, cx| { + // dock.set_open(docks.bottom.visible, cx); + // if let Some(active_panel) = docks.bottom.active_panel { + // if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { + // dock.activate_panel(ix, cx); + // } + // } + + // dock.active_panel() + // .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx)); + + // if docks.bottom.visible && docks.bottom.zoom { + // cx.focus_self() + // } + // }); + + // cx.notify(); + // })?; + + // // Serialize ourself to make sure our timestamps and any pane / item changes are replicated + // workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))?; + + // Ok(opened_items) + // }) + // } + + // #[cfg(any(test, feature = "test-support"))] + // pub fn test_new(project: ModelHandle, cx: &mut ViewContext) -> Self { + // use node_runtime::FakeNodeRuntime; + + // let client = project.read(cx).client(); + // let user_store = project.read(cx).user_store(); + + // let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx)); + // let app_state = Arc::new(AppState { + // languages: project.read(cx).languages().clone(), + // workspace_store, + // client, + // user_store, + // fs: project.read(cx).fs().clone(), + // build_window_options: |_, _, _| Default::default(), + // initialize_workspace: |_, _, _, _| Task::ready(Ok(())), + // node_runtime: FakeNodeRuntime::new(), + // }); + // Self::new(0, project, app_state, cx) + // } + + // fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option> { + // let dock = match position { + // DockPosition::Left => &self.left_dock, + // DockPosition::Right => &self.right_dock, + // DockPosition::Bottom => &self.bottom_dock, + // }; + // let active_panel = dock.read(cx).visible_panel()?; + // let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) { + // dock.read(cx).render_placeholder(cx) + // } else { + // ChildView::new(dock, cx).into_any() + // }; + + // Some( + // element + // .constrained() + // .dynamically(move |constraint, _, cx| match position { + // DockPosition::Left | DockPosition::Right => SizeConstraint::new( + // Vector2F::new(20., constraint.min.y()), + // Vector2F::new(cx.window_size().x() * 0.8, constraint.max.y()), + // ), + // DockPosition::Bottom => SizeConstraint::new( + // Vector2F::new(constraint.min.x(), 20.), + // Vector2F::new(constraint.max.x(), cx.window_size().y() * 0.8), + // ), + // }) + // .into_any(), + // ) + // } + // } + + // fn window_bounds_env_override(cx: &AsyncAppContext) -> Option { + // ZED_WINDOW_POSITION + // .zip(*ZED_WINDOW_SIZE) + // .map(|(position, size)| { + // WindowBounds::Fixed(RectF::new( + // cx.platform().screens()[0].bounds().origin() + position, + // size, + // )) + // }) + // } + + // async fn open_items( + // serialized_workspace: Option, + // workspace: &WeakViewHandle, + // mut project_paths_to_open: Vec<(PathBuf, Option)>, + // app_state: Arc, + // mut cx: AsyncAppContext, + // ) -> Result>>>> { + // let mut opened_items = Vec::with_capacity(project_paths_to_open.len()); + + // if let Some(serialized_workspace) = serialized_workspace { + // let workspace = workspace.clone(); + // let restored_items = cx + // .update(|cx| { + // Workspace::load_workspace( + // workspace, + // serialized_workspace, + // project_paths_to_open + // .iter() + // .map(|(_, project_path)| project_path) + // .cloned() + // .collect(), + // cx, + // ) + // }) + // .await?; + + // let restored_project_paths = cx.read(|cx| { + // restored_items + // .iter() + // .filter_map(|item| item.as_ref()?.project_path(cx)) + // .collect::>() + // }); + + // for restored_item in restored_items { + // opened_items.push(restored_item.map(Ok)); + // } + + // project_paths_to_open + // .iter_mut() + // .for_each(|(_, project_path)| { + // if let Some(project_path_to_open) = project_path { + // if restored_project_paths.contains(project_path_to_open) { + // *project_path = None; + // } + // } + // }); + // } else { + // for _ in 0..project_paths_to_open.len() { + // opened_items.push(None); + // } + // } + // assert!(opened_items.len() == project_paths_to_open.len()); + + // let tasks = + // project_paths_to_open + // .into_iter() + // .enumerate() + // .map(|(i, (abs_path, project_path))| { + // let workspace = workspace.clone(); + // cx.spawn(|mut cx| { + // let fs = app_state.fs.clone(); + // async move { + // let file_project_path = project_path?; + // if fs.is_file(&abs_path).await { + // Some(( + // i, + // workspace + // .update(&mut cx, |workspace, cx| { + // workspace.open_path(file_project_path, None, true, cx) + // }) + // .log_err()? + // .await, + // )) + // } else { + // None + // } + // } + // }) + // }); + + // for maybe_opened_path in futures::future::join_all(tasks.into_iter()) + // .await + // .into_iter() + // { + // if let Some((i, path_open_result)) = maybe_opened_path { + // opened_items[i] = Some(path_open_result); + // } + // } + + // Ok(opened_items) + // } + + // fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { + // const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; + // const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; + // const MESSAGE_ID: usize = 2; + + // if workspace + // .read_with(cx, |workspace, cx| { + // workspace.has_shown_notification_once::(MESSAGE_ID, cx) + // }) + // .unwrap_or(false) + // { + // return; + // } + + // if db::kvp::KEY_VALUE_STORE + // .read_kvp(NEW_DOCK_HINT_KEY) + // .ok() + // .flatten() + // .is_some() + // { + // if !workspace + // .read_with(cx, |workspace, cx| { + // workspace.has_shown_notification_once::(MESSAGE_ID, cx) + // }) + // .unwrap_or(false) + // { + // cx.update(|cx| { + // cx.update_global::(|tracker, _| { + // let entry = tracker + // .entry(TypeId::of::()) + // .or_default(); + // if !entry.contains(&MESSAGE_ID) { + // entry.push(MESSAGE_ID); + // } + // }); + // }); + // } + + // return; + // } + + // cx.spawn(|_| async move { + // db::kvp::KEY_VALUE_STORE + // .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string()) + // .await + // .ok(); + // }) + // .detach(); + + // workspace + // .update(cx, |workspace, cx| { + // workspace.show_notification_once(2, cx, |cx| { + // cx.add_view(|_| { + // MessageNotification::new_element(|text, _| { + // Text::new( + // "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.", + // text, + // ) + // .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| { + // let code_span_background_color = settings::get::(cx) + // .theme + // .editor + // .document_highlight_read_background; + + // cx.scene().push_quad(gpui::Quad { + // bounds, + // background: Some(code_span_background_color), + // border: Default::default(), + // corner_radii: (2.0).into(), + // }) + // }) + // .into_any() + // }) + // .with_click_message("Read more about the new panel system") + // .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) + // }) + // }) + // }) + // .ok(); +} + +// fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { +// const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml"; + +// workspace +// .update(cx, |workspace, cx| { +// if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { +// workspace.show_notification_once(0, cx, |cx| { +// cx.add_view(|_| { +// MessageNotification::new("Failed to load the database file.") +// .with_click_message("Click to let us know about this error") +// .on_click(|cx| cx.platform().open_url(REPORT_ISSUE_URL)) +// }) +// }); +// } +// }) +// .log_err(); +// } + +// impl Entity for Workspace { +// type Event = Event; + +// fn release(&mut self, cx: &mut AppContext) { +// self.app_state.workspace_store.update(cx, |store, _| { +// store.workspaces.remove(&self.weak_self); +// }) +// } +// } + +// impl View for Workspace { +// fn ui_name() -> &'static str { +// "Workspace" +// } + +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// let theme = theme::current(cx).clone(); +// Stack::new() +// .with_child( +// Flex::column() +// .with_child(self.render_titlebar(&theme, cx)) +// .with_child( +// Stack::new() +// .with_child({ +// let project = self.project.clone(); +// Flex::row() +// .with_children(self.render_dock(DockPosition::Left, cx)) +// .with_child( +// Flex::column() +// .with_child( +// FlexItem::new( +// self.center.render( +// &project, +// &theme, +// &self.follower_states, +// self.active_call(), +// self.active_pane(), +// self.zoomed +// .as_ref() +// .and_then(|zoomed| zoomed.upgrade(cx)) +// .as_ref(), +// &self.app_state, +// cx, +// ), +// ) +// .flex(1., true), +// ) +// .with_children( +// self.render_dock(DockPosition::Bottom, cx), +// ) +// .flex(1., true), +// ) +// .with_children(self.render_dock(DockPosition::Right, cx)) +// }) +// .with_child(Overlay::new( +// Stack::new() +// .with_children(self.zoomed.as_ref().and_then(|zoomed| { +// enum ZoomBackground {} +// let zoomed = zoomed.upgrade(cx)?; + +// let mut foreground_style = +// theme.workspace.zoomed_pane_foreground; +// if let Some(zoomed_dock_position) = self.zoomed_position { +// foreground_style = +// theme.workspace.zoomed_panel_foreground; +// let margin = foreground_style.margin.top; +// let border = foreground_style.border.top; + +// // Only include a margin and border on the opposite side. +// foreground_style.margin.top = 0.; +// foreground_style.margin.left = 0.; +// foreground_style.margin.bottom = 0.; +// foreground_style.margin.right = 0.; +// foreground_style.border.top = false; +// foreground_style.border.left = false; +// foreground_style.border.bottom = false; +// foreground_style.border.right = false; +// match zoomed_dock_position { +// DockPosition::Left => { +// foreground_style.margin.right = margin; +// foreground_style.border.right = border; +// } +// DockPosition::Right => { +// foreground_style.margin.left = margin; +// foreground_style.border.left = border; +// } +// DockPosition::Bottom => { +// foreground_style.margin.top = margin; +// foreground_style.border.top = border; +// } +// } +// } + +// Some( +// ChildView::new(&zoomed, cx) +// .contained() +// .with_style(foreground_style) +// .aligned() +// .contained() +// .with_style(theme.workspace.zoomed_background) +// .mouse::(0) +// .capture_all() +// .on_down( +// MouseButton::Left, +// |_, this: &mut Self, cx| { +// this.zoom_out(cx); +// }, +// ), +// ) +// })) +// .with_children(self.modal.as_ref().map(|modal| { +// // Prevent clicks within the modal from falling +// // through to the rest of the workspace. +// enum ModalBackground {} +// MouseEventHandler::new::( +// 0, +// cx, +// |_, cx| ChildView::new(modal.view.as_any(), cx), +// ) +// .on_click(MouseButton::Left, |_, _, _| {}) +// .contained() +// .with_style(theme.workspace.modal) +// .aligned() +// .top() +// })) +// .with_children(self.render_notifications(&theme.workspace, cx)), +// )) +// .provide_resize_bounds::() +// .flex(1.0, true), +// ) +// .with_child(ChildView::new(&self.status_bar, cx)) +// .contained() +// .with_background_color(theme.workspace.background), +// ) +// .with_children(DragAndDrop::render(cx)) +// .with_children(self.render_disconnected_overlay(cx)) +// .into_any_named("workspace") +// } + +// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { +// if cx.is_self_focused() { +// cx.focus(&self.active_pane); +// } +// } + +// fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext) -> bool { +// DragAndDrop::::update_modifiers(e.modifiers, cx) +// } +// } + +// impl WorkspaceStore { +// pub fn new(client: Arc, cx: &mut ModelContext) -> Self { +// Self { +// workspaces: Default::default(), +// followers: Default::default(), +// _subscriptions: vec![ +// client.add_request_handler(cx.handle(), Self::handle_follow), +// client.add_message_handler(cx.handle(), Self::handle_unfollow), +// client.add_message_handler(cx.handle(), Self::handle_update_followers), +// ], +// client, +// } +// } + +// pub fn update_followers( +// &self, +// project_id: Option, +// update: proto::update_followers::Variant, +// cx: &AppContext, +// ) -> Option<()> { +// if !cx.has_global::>() { +// return None; +// } + +// let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id(); +// let follower_ids: Vec<_> = self +// .followers +// .iter() +// .filter_map(|follower| { +// if follower.project_id == project_id || project_id.is_none() { +// Some(follower.peer_id.into()) +// } else { +// None +// } +// }) +// .collect(); +// if follower_ids.is_empty() { +// return None; +// } +// self.client +// .send(proto::UpdateFollowers { +// room_id, +// project_id, +// follower_ids, +// variant: Some(update), +// }) +// .log_err() +// } + +// async fn handle_follow( +// this: ModelHandle, +// envelope: TypedEnvelope, +// _: Arc, +// mut cx: AsyncAppContext, +// ) -> Result { +// this.update(&mut cx, |this, cx| { +// let follower = Follower { +// project_id: envelope.payload.project_id, +// peer_id: envelope.original_sender_id()?, +// }; +// let active_project = ActiveCall::global(cx) +// .read(cx) +// .location() +// .map(|project| project.id()); + +// let mut response = proto::FollowResponse::default(); +// for workspace in &this.workspaces { +// let Some(workspace) = workspace.upgrade(cx) else { +// continue; +// }; + +// workspace.update(cx.as_mut(), |workspace, cx| { +// let handler_response = workspace.handle_follow(follower.project_id, cx); +// if response.views.is_empty() { +// response.views = handler_response.views; +// } else { +// response.views.extend_from_slice(&handler_response.views); +// } + +// if let Some(active_view_id) = handler_response.active_view_id.clone() { +// if response.active_view_id.is_none() +// || Some(workspace.project.id()) == active_project +// { +// response.active_view_id = Some(active_view_id); +// } +// } +// }); +// } + +// if let Err(ix) = this.followers.binary_search(&follower) { +// this.followers.insert(ix, follower); +// } + +// Ok(response) +// }) +// } + +// async fn handle_unfollow( +// this: ModelHandle, +// envelope: TypedEnvelope, +// _: Arc, +// mut cx: AsyncAppContext, +// ) -> Result<()> { +// this.update(&mut cx, |this, _| { +// let follower = Follower { +// project_id: envelope.payload.project_id, +// peer_id: envelope.original_sender_id()?, +// }; +// if let Ok(ix) = this.followers.binary_search(&follower) { +// this.followers.remove(ix); +// } +// Ok(()) +// }) +// } + +// async fn handle_update_followers( +// this: ModelHandle, +// envelope: TypedEnvelope, +// _: Arc, +// mut cx: AsyncAppContext, +// ) -> Result<()> { +// let leader_id = envelope.original_sender_id()?; +// let update = envelope.payload; +// this.update(&mut cx, |this, cx| { +// for workspace in &this.workspaces { +// let Some(workspace) = workspace.upgrade(cx) else { +// continue; +// }; +// workspace.update(cx.as_mut(), |workspace, cx| { +// let project_id = workspace.project.read(cx).remote_id(); +// if update.project_id != project_id && update.project_id.is_some() { +// return; +// } +// workspace.handle_update_followers(leader_id, update.clone(), cx); +// }); +// } +// Ok(()) +// }) +// } +// } + +// impl Entity for WorkspaceStore { +// type Event = (); +// } + +// impl ViewId { +// pub(crate) fn from_proto(message: proto::ViewId) -> Result { +// Ok(Self { +// creator: message +// .creator +// .ok_or_else(|| anyhow!("creator is missing"))?, +// id: message.id, +// }) +// } + +// pub(crate) fn to_proto(&self) -> proto::ViewId { +// proto::ViewId { +// creator: Some(self.creator), +// id: self.id, +// } +// } +// } + +// pub trait WorkspaceHandle { +// fn file_project_paths(&self, cx: &AppContext) -> Vec; +// } + +// impl WorkspaceHandle for View { +// fn file_project_paths(&self, cx: &AppContext) -> Vec { +// self.read(cx) +// .worktrees(cx) +// .flat_map(|worktree| { +// let worktree_id = worktree.read(cx).id(); +// worktree.read(cx).files(true, 0).map(move |f| ProjectPath { +// worktree_id, +// path: f.path.clone(), +// }) +// }) +// .collect::>() +// } +// } + +// impl std::fmt::Debug for OpenPaths { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// f.debug_struct("OpenPaths") +// .field("paths", &self.paths) +// .finish() +// } +// } + +// pub struct WorkspaceCreated(pub WeakViewHandle); + +pub async fn activate_workspace_for_project( + cx: &mut AsyncAppContext, + predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static, +) -> Option> { + cx.run_on_main(move |cx| { + for window in cx.windows() { + let Some(workspace) = window.downcast::() else { + continue; + }; + + let predicate = cx + .update_window_root(&workspace, |workspace, cx| { + let project = workspace.project.read(cx); + if predicate(project, cx) { + cx.activate_window(); + true + } else { + false + } + }) + .log_err() + .unwrap_or(false); + + if predicate { + return Some(workspace); + } + } + + None + }) + .ok()? + .await +} + +// pub async fn last_opened_workspace_paths() -> Option { +// DB.last_workspace().await.log_err().flatten() +// } + +// async fn join_channel_internal( +// channel_id: u64, +// app_state: &Arc, +// requesting_window: Option>, +// active_call: &ModelHandle, +// cx: &mut AsyncAppContext, +// ) -> Result { +// let (should_prompt, open_room) = active_call.read_with(cx, |active_call, cx| { +// let Some(room) = active_call.room().map(|room| room.read(cx)) else { +// return (false, None); +// }; + +// let already_in_channel = room.channel_id() == Some(channel_id); +// let should_prompt = room.is_sharing_project() +// && room.remote_participants().len() > 0 +// && !already_in_channel; +// let open_room = if already_in_channel { +// active_call.room().cloned() +// } else { +// None +// }; +// (should_prompt, open_room) +// }); + +// if let Some(room) = open_room { +// let task = room.update(cx, |room, cx| { +// if let Some((project, host)) = room.most_active_project(cx) { +// return Some(join_remote_project(project, host, app_state.clone(), cx)); +// } + +// None +// }); +// if let Some(task) = task { +// task.await?; +// } +// return anyhow::Ok(true); +// } + +// if should_prompt { +// if let Some(workspace) = requesting_window { +// if let Some(window) = workspace.update(cx, |cx| cx.window()) { +// let answer = window.prompt( +// PromptLevel::Warning, +// "Leaving this call will unshare your current project.\nDo you want to switch channels?", +// &["Yes, Join Channel", "Cancel"], +// cx, +// ); + +// if let Some(mut answer) = answer { +// if answer.next().await == Some(1) { +// return Ok(false); +// } +// } +// } else { +// return Ok(false); // unreachable!() hopefully +// } +// } else { +// return Ok(false); // unreachable!() hopefully +// } +// } + +// let client = cx.read(|cx| active_call.read(cx).client()); + +// let mut client_status = client.status(); + +// // this loop will terminate within client::CONNECTION_TIMEOUT seconds. +// 'outer: loop { +// let Some(status) = client_status.recv().await else { +// return Err(anyhow!("error connecting")); +// }; + +// match status { +// Status::Connecting +// | Status::Authenticating +// | Status::Reconnecting +// | Status::Reauthenticating => continue, +// Status::Connected { .. } => break 'outer, +// Status::SignedOut => return Err(anyhow!("not signed in")), +// Status::UpgradeRequired => return Err(anyhow!("zed is out of date")), +// Status::ConnectionError | Status::ConnectionLost | Status::ReconnectionError { .. } => { +// return Err(anyhow!("zed is offline")) +// } +// } +// } + +// let room = active_call +// .update(cx, |active_call, cx| { +// active_call.join_channel(channel_id, cx) +// }) +// .await?; + +// room.update(cx, |room, _| room.room_update_completed()) +// .await; + +// let task = room.update(cx, |room, cx| { +// if let Some((project, host)) = room.most_active_project(cx) { +// return Some(join_remote_project(project, host, app_state.clone(), cx)); +// } + +// None +// }); +// if let Some(task) = task { +// task.await?; +// return anyhow::Ok(true); +// } +// anyhow::Ok(false) +// } + +// pub fn join_channel( +// channel_id: u64, +// app_state: Arc, +// requesting_window: Option>, +// cx: &mut AppContext, +// ) -> Task> { +// let active_call = ActiveCall::global(cx); +// cx.spawn(|mut cx| async move { +// let result = join_channel_internal( +// channel_id, +// &app_state, +// requesting_window, +// &active_call, +// &mut cx, +// ) +// .await; + +// // join channel succeeded, and opened a window +// if matches!(result, Ok(true)) { +// return anyhow::Ok(()); +// } + +// if requesting_window.is_some() { +// return anyhow::Ok(()); +// } + +// // find an existing workspace to focus and show call controls +// let mut active_window = activate_any_workspace_window(&mut cx); +// if active_window.is_none() { +// // no open workspaces, make one to show the error in (blergh) +// cx.update(|cx| Workspace::new_local(vec![], app_state.clone(), requesting_window, cx)) +// .await; +// } + +// active_window = activate_any_workspace_window(&mut cx); +// if active_window.is_none() { +// return result.map(|_| ()); // unreachable!() assuming new_local always opens a window +// } + +// if let Err(err) = result { +// let prompt = active_window.unwrap().prompt( +// PromptLevel::Critical, +// &format!("Failed to join channel: {}", err), +// &["Ok"], +// &mut cx, +// ); +// if let Some(mut prompt) = prompt { +// prompt.next().await; +// } else { +// return Err(err); +// } +// } + +// // return ok, we showed the error to the user. +// return anyhow::Ok(()); +// }) +// } + +// pub fn activate_any_workspace_window(cx: &mut AsyncAppContext) -> Option { +// for window in cx.windows() { +// let found = window.update(cx, |cx| { +// let is_workspace = cx.root_view().clone().downcast::().is_some(); +// if is_workspace { +// cx.activate_window(); +// } +// is_workspace +// }); +// if found == Some(true) { +// return Some(window); +// } +// } +// None +// } + +use client2::{ + proto::{self, PeerId, ViewId}, + Client, UserStore, +}; +use collections::{HashMap, HashSet}; +use gpui2::{ + AnyHandle, AnyView, AppContext, AsyncAppContext, DisplayId, Handle, MainThread, Task, View, + ViewContext, WeakHandle, WeakView, WindowBounds, WindowHandle, WindowOptions, +}; +use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; +use language2::LanguageRegistry; +use node_runtime::NodeRuntime; +use project2::{Project, ProjectEntryId, ProjectPath, Worktree}; +use std::{ + any::TypeId, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, +}; +use util::ResultExt; + +#[allow(clippy::type_complexity)] +pub fn open_paths( + abs_paths: &[PathBuf], + app_state: &Arc, + requesting_window: Option>, + cx: &mut AppContext, +) -> Task< + anyhow::Result<( + WindowHandle, + Vec, anyhow::Error>>>, + )>, +> { + let app_state = app_state.clone(); + let abs_paths = abs_paths.to_vec(); + cx.spawn(|mut cx| async move { + // Open paths in existing workspace if possible + let existing = activate_workspace_for_project(&mut cx, |project, cx| { + project.contains_paths(&abs_paths, cx) + }) + .await; + + if let Some(existing) = existing { + Ok(( + existing.clone(), + cx.update_window_root(&existing, |workspace, cx| { + workspace.open_paths(abs_paths, true, cx) + })? + .await, + )) + } else { + todo!() + // Ok(cx + // .update(|cx| { + // Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx) + // }) + // .await) + } + }) +} + +// pub fn open_new( +// app_state: &Arc, +// cx: &mut AppContext, +// init: impl FnOnce(&mut Workspace, &mut ViewContext) + 'static, +// ) -> Task<()> { +// let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx); +// cx.spawn(|mut cx| async move { +// let (workspace, opened_paths) = task.await; + +// workspace +// .update(&mut cx, |workspace, cx| { +// if opened_paths.is_empty() { +// init(workspace, cx) +// } +// }) +// .log_err(); +// }) +// } + +// pub fn create_and_open_local_file( +// path: &'static Path, +// cx: &mut ViewContext, +// default_content: impl 'static + Send + FnOnce() -> Rope, +// ) -> Task>> { +// cx.spawn(|workspace, mut cx| async move { +// let fs = workspace.read_with(&cx, |workspace, _| workspace.app_state().fs.clone())?; +// if !fs.is_file(path).await { +// fs.create_file(path, Default::default()).await?; +// fs.save(path, &default_content(), Default::default()) +// .await?; +// } + +// let mut items = workspace +// .update(&mut cx, |workspace, cx| { +// workspace.with_local_workspace(cx, |workspace, cx| { +// workspace.open_paths(vec![path.to_path_buf()], false, cx) +// }) +// })? +// .await? +// .await; + +// let item = items.pop().flatten(); +// item.ok_or_else(|| anyhow!("path {path:?} is not a file"))? +// }) +// } + +// pub fn join_remote_project( +// project_id: u64, +// follow_user_id: u64, +// app_state: Arc, +// cx: &mut AppContext, +// ) -> Task> { +// cx.spawn(|mut cx| async move { +// let windows = cx.windows(); +// let existing_workspace = windows.into_iter().find_map(|window| { +// window.downcast::().and_then(|window| { +// window +// .read_root_with(&cx, |workspace, cx| { +// if workspace.project().read(cx).remote_id() == Some(project_id) { +// Some(cx.handle().downgrade()) +// } else { +// None +// } +// }) +// .unwrap_or(None) +// }) +// }); + +// let workspace = if let Some(existing_workspace) = existing_workspace { +// existing_workspace +// } else { +// let active_call = cx.read(ActiveCall::global); +// let room = active_call +// .read_with(&cx, |call, _| call.room().cloned()) +// .ok_or_else(|| anyhow!("not in a call"))?; +// let project = room +// .update(&mut cx, |room, cx| { +// room.join_project( +// project_id, +// app_state.languages.clone(), +// app_state.fs.clone(), +// cx, +// ) +// }) +// .await?; + +// let window_bounds_override = window_bounds_env_override(&cx); +// let window = cx.add_window( +// (app_state.build_window_options)( +// window_bounds_override, +// None, +// cx.platform().as_ref(), +// ), +// |cx| Workspace::new(0, project, app_state.clone(), cx), +// ); +// let workspace = window.root(&cx).unwrap(); +// (app_state.initialize_workspace)( +// workspace.downgrade(), +// false, +// app_state.clone(), +// cx.clone(), +// ) +// .await +// .log_err(); + +// workspace.downgrade() +// }; + +// workspace.window().activate(&mut cx); +// cx.platform().activate(true); + +// workspace.update(&mut cx, |workspace, cx| { +// if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { +// let follow_peer_id = room +// .read(cx) +// .remote_participants() +// .iter() +// .find(|(_, participant)| participant.user.id == follow_user_id) +// .map(|(_, p)| p.peer_id) +// .or_else(|| { +// // If we couldn't follow the given user, follow the host instead. +// let collaborator = workspace +// .project() +// .read(cx) +// .collaborators() +// .values() +// .find(|collaborator| collaborator.replica_id == 0)?; +// Some(collaborator.peer_id) +// }); + +// if let Some(follow_peer_id) = follow_peer_id { +// workspace +// .follow(follow_peer_id, cx) +// .map(|follow| follow.detach_and_log_err(cx)); +// } +// } +// })?; + +// anyhow::Ok(()) +// }) +// } + +// pub fn restart(_: &Restart, cx: &mut AppContext) { +// let should_confirm = settings::get::(cx).confirm_quit; +// cx.spawn(|mut cx| async move { +// let mut workspace_windows = cx +// .windows() +// .into_iter() +// .filter_map(|window| window.downcast::()) +// .collect::>(); + +// // If multiple windows have unsaved changes, and need a save prompt, +// // prompt in the active window before switching to a different window. +// workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); + +// if let (true, Some(window)) = (should_confirm, workspace_windows.first()) { +// let answer = window.prompt( +// PromptLevel::Info, +// "Are you sure you want to restart?", +// &["Restart", "Cancel"], +// &mut cx, +// ); + +// if let Some(mut answer) = answer { +// let answer = answer.next().await; +// if answer != Some(0) { +// return Ok(()); +// } +// } +// } + +// // If the user cancels any save prompt, then keep the app open. +// for window in workspace_windows { +// if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| { +// workspace.prepare_to_close(true, cx) +// }) { +// if !should_close.await? { +// return Ok(()); +// } +// } +// } +// cx.platform().restart(); +// anyhow::Ok(()) +// }) +// .detach_and_log_err(cx); +// } + +// fn parse_pixel_position_env_var(value: &str) -> Option { +// let mut parts = value.split(','); +// let width: usize = parts.next()?.parse().ok()?; +// let height: usize = parts.next()?.parse().ok()?; +// Some(vec2f(width as f32, height as f32)) +// } + +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::{ +// dock::test::{TestPanel, TestPanelEvent}, +// item::test::{TestItem, TestItemEvent, TestProjectItem}, +// }; +// use fs::FakeFs; +// use gpui::{executor::Deterministic, test::EmptyView, TestAppContext}; +// use project::{Project, ProjectEntryId}; +// use serde_json::json; +// use settings::SettingsStore; +// use std::{cell::RefCell, rc::Rc}; + +// #[gpui::test] +// async fn test_tab_disambiguation(cx: &mut TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); +// let project = Project::test(fs, [], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); + +// // Adding an item with no ambiguity renders the tab without detail. +// let item1 = window.add_view(cx, |_| { +// let mut item = TestItem::new(); +// item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]); +// item +// }); +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item1.clone()), cx); +// }); +// item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), None)); + +// // Adding an item that creates ambiguity increases the level of detail on +// // both tabs. +// let item2 = window.add_view(cx, |_| { +// let mut item = TestItem::new(); +// item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); +// item +// }); +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item2.clone()), cx); +// }); +// item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1))); +// item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1))); + +// // Adding an item that creates ambiguity increases the level of detail only +// // on the ambiguous tabs. In this case, the ambiguity can't be resolved so +// // we stop at the highest detail available. +// let item3 = window.add_view(cx, |_| { +// let mut item = TestItem::new(); +// item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); +// item +// }); +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item3.clone()), cx); +// }); +// item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1))); +// item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3))); +// item3.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3))); +// } + +// #[gpui::test] +// async fn test_tracking_active_path(cx: &mut TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); +// fs.insert_tree( +// "/root1", +// json!({ +// "one.txt": "", +// "two.txt": "", +// }), +// ) +// .await; +// fs.insert_tree( +// "/root2", +// json!({ +// "three.txt": "", +// }), +// ) +// .await; + +// let project = Project::test(fs, ["root1".as_ref()], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); +// let worktree_id = project.read_with(cx, |project, cx| { +// project.worktrees(cx).next().unwrap().read(cx).id() +// }); + +// let item1 = window.add_view(cx, |cx| { +// TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]) +// }); +// let item2 = window.add_view(cx, |cx| { +// TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)]) +// }); + +// // Add an item to an empty pane +// workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item1), cx)); +// project.read_with(cx, |project, cx| { +// assert_eq!( +// project.active_entry(), +// project +// .entry_for_path(&(worktree_id, "one.txt").into(), cx) +// .map(|e| e.id) +// ); +// }); +// assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1")); + +// // Add a second item to a non-empty pane +// workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); +// assert_eq!(window.current_title(cx).as_deref(), Some("two.txt — root1")); +// project.read_with(cx, |project, cx| { +// assert_eq!( +// project.active_entry(), +// project +// .entry_for_path(&(worktree_id, "two.txt").into(), cx) +// .map(|e| e.id) +// ); +// }); + +// // Close the active item +// pane.update(cx, |pane, cx| { +// pane.close_active_item(&Default::default(), cx).unwrap() +// }) +// .await +// .unwrap(); +// assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1")); +// project.read_with(cx, |project, cx| { +// assert_eq!( +// project.active_entry(), +// project +// .entry_for_path(&(worktree_id, "one.txt").into(), cx) +// .map(|e| e.id) +// ); +// }); + +// // Add a project folder +// project +// .update(cx, |project, cx| { +// project.find_or_create_local_worktree("/root2", true, cx) +// }) +// .await +// .unwrap(); +// assert_eq!( +// window.current_title(cx).as_deref(), +// Some("one.txt — root1, root2") +// ); + +// // Remove a project folder +// project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); +// assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root2")); +// } + +// #[gpui::test] +// async fn test_close_window(cx: &mut TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); +// fs.insert_tree("/root", json!({ "one": "" })).await; + +// let project = Project::test(fs, ["root".as_ref()], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); +// let workspace = window.root(cx); + +// // When there are no dirty items, there's nothing to do. +// let item1 = window.add_view(cx, |_| TestItem::new()); +// workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx)); +// let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); +// assert!(task.await.unwrap()); + +// // When there are dirty untitled items, prompt to save each one. If the user +// // cancels any prompt, then abort. +// let item2 = window.add_view(cx, |_| TestItem::new().with_dirty(true)); +// let item3 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) +// }); +// workspace.update(cx, |w, cx| { +// w.add_item(Box::new(item2.clone()), cx); +// w.add_item(Box::new(item3.clone()), cx); +// }); +// let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); +// cx.foreground().run_until_parked(); +// window.simulate_prompt_answer(2, cx); // cancel save all +// cx.foreground().run_until_parked(); +// window.simulate_prompt_answer(2, cx); // cancel save all +// cx.foreground().run_until_parked(); +// assert!(!window.has_pending_prompt(cx)); +// assert!(!task.await.unwrap()); +// } + +// #[gpui::test] +// async fn test_close_pane_items(cx: &mut TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, None, cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); + +// let item1 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) +// }); +// let item2 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_conflict(true) +// .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]) +// }); +// let item3 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_conflict(true) +// .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)]) +// }); +// let item4 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_project_items(&[TestProjectItem::new_untitled(cx)]) +// }); +// let pane = workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item1.clone()), cx); +// workspace.add_item(Box::new(item2.clone()), cx); +// workspace.add_item(Box::new(item3.clone()), cx); +// workspace.add_item(Box::new(item4.clone()), cx); +// workspace.active_pane().clone() +// }); + +// let close_items = pane.update(cx, |pane, cx| { +// pane.activate_item(1, true, true, cx); +// assert_eq!(pane.active_item().unwrap().id(), item2.id()); +// let item1_id = item1.id(); +// let item3_id = item3.id(); +// let item4_id = item4.id(); +// pane.close_items(cx, SaveIntent::Close, move |id| { +// [item1_id, item3_id, item4_id].contains(&id) +// }) +// }); +// cx.foreground().run_until_parked(); + +// assert!(window.has_pending_prompt(cx)); +// // Ignore "Save all" prompt +// window.simulate_prompt_answer(2, cx); +// cx.foreground().run_until_parked(); +// // There's a prompt to save item 1. +// pane.read_with(cx, |pane, _| { +// assert_eq!(pane.items_len(), 4); +// assert_eq!(pane.active_item().unwrap().id(), item1.id()); +// }); +// // Confirm saving item 1. +// window.simulate_prompt_answer(0, cx); +// cx.foreground().run_until_parked(); + +// // Item 1 is saved. There's a prompt to save item 3. +// pane.read_with(cx, |pane, cx| { +// assert_eq!(item1.read(cx).save_count, 1); +// assert_eq!(item1.read(cx).save_as_count, 0); +// assert_eq!(item1.read(cx).reload_count, 0); +// assert_eq!(pane.items_len(), 3); +// assert_eq!(pane.active_item().unwrap().id(), item3.id()); +// }); +// assert!(window.has_pending_prompt(cx)); + +// // Cancel saving item 3. +// window.simulate_prompt_answer(1, cx); +// cx.foreground().run_until_parked(); + +// // Item 3 is reloaded. There's a prompt to save item 4. +// pane.read_with(cx, |pane, cx| { +// assert_eq!(item3.read(cx).save_count, 0); +// assert_eq!(item3.read(cx).save_as_count, 0); +// assert_eq!(item3.read(cx).reload_count, 1); +// assert_eq!(pane.items_len(), 2); +// assert_eq!(pane.active_item().unwrap().id(), item4.id()); +// }); +// assert!(window.has_pending_prompt(cx)); + +// // Confirm saving item 4. +// window.simulate_prompt_answer(0, cx); +// cx.foreground().run_until_parked(); + +// // There's a prompt for a path for item 4. +// cx.simulate_new_path_selection(|_| Some(Default::default())); +// close_items.await.unwrap(); + +// // The requested items are closed. +// pane.read_with(cx, |pane, cx| { +// assert_eq!(item4.read(cx).save_count, 0); +// assert_eq!(item4.read(cx).save_as_count, 1); +// assert_eq!(item4.read(cx).reload_count, 0); +// assert_eq!(pane.items_len(), 1); +// assert_eq!(pane.active_item().unwrap().id(), item2.id()); +// }); +// } + +// #[gpui::test] +// async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, [], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); + +// // Create several workspace items with single project entries, and two +// // workspace items with multiple project entries. +// let single_entry_items = (0..=4) +// .map(|project_entry_id| { +// window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_project_items(&[TestProjectItem::new( +// project_entry_id, +// &format!("{project_entry_id}.txt"), +// cx, +// )]) +// }) +// }) +// .collect::>(); +// let item_2_3 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_singleton(false) +// .with_project_items(&[ +// single_entry_items[2].read(cx).project_items[0].clone(), +// single_entry_items[3].read(cx).project_items[0].clone(), +// ]) +// }); +// let item_3_4 = window.add_view(cx, |cx| { +// TestItem::new() +// .with_dirty(true) +// .with_singleton(false) +// .with_project_items(&[ +// single_entry_items[3].read(cx).project_items[0].clone(), +// single_entry_items[4].read(cx).project_items[0].clone(), +// ]) +// }); + +// // Create two panes that contain the following project entries: +// // left pane: +// // multi-entry items: (2, 3) +// // single-entry items: 0, 1, 2, 3, 4 +// // right pane: +// // single-entry items: 1 +// // multi-entry items: (3, 4) +// let left_pane = workspace.update(cx, |workspace, cx| { +// let left_pane = workspace.active_pane().clone(); +// workspace.add_item(Box::new(item_2_3.clone()), cx); +// for item in single_entry_items { +// workspace.add_item(Box::new(item), cx); +// } +// left_pane.update(cx, |pane, cx| { +// pane.activate_item(2, true, true, cx); +// }); + +// workspace +// .split_and_clone(left_pane.clone(), SplitDirection::Right, cx) +// .unwrap(); + +// left_pane +// }); + +// //Need to cause an effect flush in order to respect new focus +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item_3_4.clone()), cx); +// cx.focus(&left_pane); +// }); + +// // When closing all of the items in the left pane, we should be prompted twice: +// // once for project entry 0, and once for project entry 2. After those two +// // prompts, the task should complete. + +// let close = left_pane.update(cx, |pane, cx| { +// pane.close_items(cx, SaveIntent::Close, move |_| true) +// }); +// cx.foreground().run_until_parked(); +// // Discard "Save all" prompt +// window.simulate_prompt_answer(2, cx); + +// cx.foreground().run_until_parked(); +// left_pane.read_with(cx, |pane, cx| { +// assert_eq!( +// pane.active_item().unwrap().project_entry_ids(cx).as_slice(), +// &[ProjectEntryId::from_proto(0)] +// ); +// }); +// window.simulate_prompt_answer(0, cx); + +// cx.foreground().run_until_parked(); +// left_pane.read_with(cx, |pane, cx| { +// assert_eq!( +// pane.active_item().unwrap().project_entry_ids(cx).as_slice(), +// &[ProjectEntryId::from_proto(2)] +// ); +// }); +// window.simulate_prompt_answer(0, cx); + +// cx.foreground().run_until_parked(); +// close.await.unwrap(); +// left_pane.read_with(cx, |pane, _| { +// assert_eq!(pane.items_len(), 0); +// }); +// } + +// #[gpui::test] +// async fn test_autosave(deterministic: Arc, cx: &mut gpui::TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, [], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + +// let item = window.add_view(cx, |cx| { +// TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) +// }); +// let item_id = item.id(); +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item.clone()), cx); +// }); + +// // Autosave on window change. +// item.update(cx, |item, cx| { +// cx.update_global(|settings: &mut SettingsStore, cx| { +// settings.update_user_settings::(cx, |settings| { +// settings.autosave = Some(AutosaveSetting::OnWindowChange); +// }) +// }); +// item.is_dirty = true; +// }); + +// // Deactivating the window saves the file. +// window.simulate_deactivation(cx); +// deterministic.run_until_parked(); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 1)); + +// // Autosave on focus change. +// item.update(cx, |item, cx| { +// cx.focus_self(); +// cx.update_global(|settings: &mut SettingsStore, cx| { +// settings.update_user_settings::(cx, |settings| { +// settings.autosave = Some(AutosaveSetting::OnFocusChange); +// }) +// }); +// item.is_dirty = true; +// }); + +// // Blurring the item saves the file. +// item.update(cx, |_, cx| cx.blur()); +// deterministic.run_until_parked(); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); + +// // Deactivating the window still saves the file. +// window.simulate_activation(cx); +// item.update(cx, |item, cx| { +// cx.focus_self(); +// item.is_dirty = true; +// }); +// window.simulate_deactivation(cx); + +// deterministic.run_until_parked(); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 3)); + +// // Autosave after delay. +// item.update(cx, |item, cx| { +// cx.update_global(|settings: &mut SettingsStore, cx| { +// settings.update_user_settings::(cx, |settings| { +// settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 }); +// }) +// }); +// item.is_dirty = true; +// cx.emit(TestItemEvent::Edit); +// }); + +// // Delay hasn't fully expired, so the file is still dirty and unsaved. +// deterministic.advance_clock(Duration::from_millis(250)); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 3)); + +// // After delay expires, the file is saved. +// deterministic.advance_clock(Duration::from_millis(250)); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 4)); + +// // Autosave on focus change, ensuring closing the tab counts as such. +// item.update(cx, |item, cx| { +// cx.update_global(|settings: &mut SettingsStore, cx| { +// settings.update_user_settings::(cx, |settings| { +// settings.autosave = Some(AutosaveSetting::OnFocusChange); +// }) +// }); +// item.is_dirty = true; +// }); + +// pane.update(cx, |pane, cx| { +// pane.close_items(cx, SaveIntent::Close, move |id| id == item_id) +// }) +// .await +// .unwrap(); +// assert!(!window.has_pending_prompt(cx)); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); + +// // Add the item again, ensuring autosave is prevented if the underlying file has been deleted. +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item.clone()), cx); +// }); +// item.update(cx, |item, cx| { +// item.project_items[0].update(cx, |item, _| { +// item.entry_id = None; +// }); +// item.is_dirty = true; +// cx.blur(); +// }); +// deterministic.run_until_parked(); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); + +// // Ensure autosave is prevented for deleted files also when closing the buffer. +// let _close_items = pane.update(cx, |pane, cx| { +// pane.close_items(cx, SaveIntent::Close, move |id| id == item_id) +// }); +// deterministic.run_until_parked(); +// assert!(window.has_pending_prompt(cx)); +// item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); +// } + +// #[gpui::test] +// async fn test_pane_navigation(cx: &mut gpui::TestAppContext) { +// init_test(cx); + +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, [], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); + +// let item = window.add_view(cx, |cx| { +// TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) +// }); +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); +// let toolbar = pane.read_with(cx, |pane, _| pane.toolbar().clone()); +// let toolbar_notify_count = Rc::new(RefCell::new(0)); + +// workspace.update(cx, |workspace, cx| { +// workspace.add_item(Box::new(item.clone()), cx); +// let toolbar_notification_count = toolbar_notify_count.clone(); +// cx.observe(&toolbar, move |_, _, _| { +// *toolbar_notification_count.borrow_mut() += 1 +// }) +// .detach(); +// }); + +// pane.read_with(cx, |pane, _| { +// assert!(!pane.can_navigate_backward()); +// assert!(!pane.can_navigate_forward()); +// }); + +// item.update(cx, |item, cx| { +// item.set_state("one".to_string(), cx); +// }); + +// // Toolbar must be notified to re-render the navigation buttons +// assert_eq!(*toolbar_notify_count.borrow(), 1); + +// pane.read_with(cx, |pane, _| { +// assert!(pane.can_navigate_backward()); +// assert!(!pane.can_navigate_forward()); +// }); + +// workspace +// .update(cx, |workspace, cx| workspace.go_back(pane.downgrade(), cx)) +// .await +// .unwrap(); + +// assert_eq!(*toolbar_notify_count.borrow(), 3); +// pane.read_with(cx, |pane, _| { +// assert!(!pane.can_navigate_backward()); +// assert!(pane.can_navigate_forward()); +// }); +// } + +// #[gpui::test] +// async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, [], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); + +// let panel = workspace.update(cx, |workspace, cx| { +// let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right)); +// workspace.add_panel(panel.clone(), cx); + +// workspace +// .right_dock() +// .update(cx, |right_dock, cx| right_dock.set_open(true, cx)); + +// panel +// }); + +// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); +// pane.update(cx, |pane, cx| { +// let item = cx.add_view(|_| TestItem::new()); +// pane.add_item(Box::new(item), true, true, None, cx); +// }); + +// // Transfer focus from center to panel +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_panel_focus::(cx); +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(!panel.is_zoomed(cx)); +// assert!(panel.has_focus(cx)); +// }); + +// // Transfer focus from panel to center +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_panel_focus::(cx); +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(!panel.is_zoomed(cx)); +// assert!(!panel.has_focus(cx)); +// }); + +// // Close the dock +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_dock(DockPosition::Right, cx); +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(!workspace.right_dock().read(cx).is_open()); +// assert!(!panel.is_zoomed(cx)); +// assert!(!panel.has_focus(cx)); +// }); + +// // Open the dock +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_dock(DockPosition::Right, cx); +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(!panel.is_zoomed(cx)); +// assert!(panel.has_focus(cx)); +// }); + +// // Focus and zoom panel +// panel.update(cx, |panel, cx| { +// cx.focus_self(); +// panel.set_zoomed(true, cx) +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(panel.is_zoomed(cx)); +// assert!(panel.has_focus(cx)); +// }); + +// // Transfer focus to the center closes the dock +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_panel_focus::(cx); +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(!workspace.right_dock().read(cx).is_open()); +// assert!(panel.is_zoomed(cx)); +// assert!(!panel.has_focus(cx)); +// }); + +// // Transferring focus back to the panel keeps it zoomed +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_panel_focus::(cx); +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(panel.is_zoomed(cx)); +// assert!(panel.has_focus(cx)); +// }); + +// // Close the dock while it is zoomed +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_dock(DockPosition::Right, cx) +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(!workspace.right_dock().read(cx).is_open()); +// assert!(panel.is_zoomed(cx)); +// assert!(workspace.zoomed.is_none()); +// assert!(!panel.has_focus(cx)); +// }); + +// // Opening the dock, when it's zoomed, retains focus +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_dock(DockPosition::Right, cx) +// }); + +// workspace.read_with(cx, |workspace, cx| { +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(panel.is_zoomed(cx)); +// assert!(workspace.zoomed.is_some()); +// assert!(panel.has_focus(cx)); +// }); + +// // Unzoom and close the panel, zoom the active pane. +// panel.update(cx, |panel, cx| panel.set_zoomed(false, cx)); +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_dock(DockPosition::Right, cx) +// }); +// pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx)); + +// // Opening a dock unzooms the pane. +// workspace.update(cx, |workspace, cx| { +// workspace.toggle_dock(DockPosition::Right, cx) +// }); +// workspace.read_with(cx, |workspace, cx| { +// let pane = pane.read(cx); +// assert!(!pane.is_zoomed()); +// assert!(!pane.has_focus()); +// assert!(workspace.right_dock().read(cx).is_open()); +// assert!(workspace.zoomed.is_none()); +// }); +// } + +// #[gpui::test] +// async fn test_panels(cx: &mut gpui::TestAppContext) { +// init_test(cx); +// let fs = FakeFs::new(cx.background()); + +// let project = Project::test(fs, [], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); + +// let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| { +// // Add panel_1 on the left, panel_2 on the right. +// let panel_1 = cx.add_view(|_| TestPanel::new(DockPosition::Left)); +// workspace.add_panel(panel_1.clone(), cx); +// workspace +// .left_dock() +// .update(cx, |left_dock, cx| left_dock.set_open(true, cx)); +// let panel_2 = cx.add_view(|_| TestPanel::new(DockPosition::Right)); +// workspace.add_panel(panel_2.clone(), cx); +// workspace +// .right_dock() +// .update(cx, |right_dock, cx| right_dock.set_open(true, cx)); + +// let left_dock = workspace.left_dock(); +// assert_eq!( +// left_dock.read(cx).visible_panel().unwrap().id(), +// panel_1.id() +// ); +// assert_eq!( +// left_dock.read(cx).active_panel_size(cx).unwrap(), +// panel_1.size(cx) +// ); + +// left_dock.update(cx, |left_dock, cx| { +// left_dock.resize_active_panel(Some(1337.), cx) +// }); +// assert_eq!( +// workspace +// .right_dock() +// .read(cx) +// .visible_panel() +// .unwrap() +// .id(), +// panel_2.id() +// ); + +// (panel_1, panel_2) +// }); + +// // Move panel_1 to the right +// panel_1.update(cx, |panel_1, cx| { +// panel_1.set_position(DockPosition::Right, cx) +// }); + +// workspace.update(cx, |workspace, cx| { +// // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right. +// // Since it was the only panel on the left, the left dock should now be closed. +// assert!(!workspace.left_dock().read(cx).is_open()); +// assert!(workspace.left_dock().read(cx).visible_panel().is_none()); +// let right_dock = workspace.right_dock(); +// assert_eq!( +// right_dock.read(cx).visible_panel().unwrap().id(), +// panel_1.id() +// ); +// assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); + +// // Now we move panel_2 to the left +// panel_2.set_position(DockPosition::Left, cx); +// }); + +// workspace.update(cx, |workspace, cx| { +// // Since panel_2 was not visible on the right, we don't open the left dock. +// assert!(!workspace.left_dock().read(cx).is_open()); +// // And the right dock is unaffected in it's displaying of panel_1 +// assert!(workspace.right_dock().read(cx).is_open()); +// assert_eq!( +// workspace +// .right_dock() +// .read(cx) +// .visible_panel() +// .unwrap() +// .id(), +// panel_1.id() +// ); +// }); + +// // Move panel_1 back to the left +// panel_1.update(cx, |panel_1, cx| { +// panel_1.set_position(DockPosition::Left, cx) +// }); + +// workspace.update(cx, |workspace, cx| { +// // Since panel_1 was visible on the right, we open the left dock and make panel_1 active. +// let left_dock = workspace.left_dock(); +// assert!(left_dock.read(cx).is_open()); +// assert_eq!( +// left_dock.read(cx).visible_panel().unwrap().id(), +// panel_1.id() +// ); +// assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); +// // And right the dock should be closed as it no longer has any panels. +// assert!(!workspace.right_dock().read(cx).is_open()); + +// // Now we move panel_1 to the bottom +// panel_1.set_position(DockPosition::Bottom, cx); +// }); + +// workspace.update(cx, |workspace, cx| { +// // Since panel_1 was visible on the left, we close the left dock. +// assert!(!workspace.left_dock().read(cx).is_open()); +// // The bottom dock is sized based on the panel's default size, +// // since the panel orientation changed from vertical to horizontal. +// let bottom_dock = workspace.bottom_dock(); +// assert_eq!( +// bottom_dock.read(cx).active_panel_size(cx).unwrap(), +// panel_1.size(cx), +// ); +// // Close bottom dock and move panel_1 back to the left. +// bottom_dock.update(cx, |bottom_dock, cx| bottom_dock.set_open(false, cx)); +// panel_1.set_position(DockPosition::Left, cx); +// }); + +// // Emit activated event on panel 1 +// panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Activated)); + +// // Now the left dock is open and panel_1 is active and focused. +// workspace.read_with(cx, |workspace, cx| { +// let left_dock = workspace.left_dock(); +// assert!(left_dock.read(cx).is_open()); +// assert_eq!( +// left_dock.read(cx).visible_panel().unwrap().id(), +// panel_1.id() +// ); +// assert!(panel_1.is_focused(cx)); +// }); + +// // Emit closed event on panel 2, which is not active +// panel_2.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed)); + +// // Wo don't close the left dock, because panel_2 wasn't the active panel +// workspace.read_with(cx, |workspace, cx| { +// let left_dock = workspace.left_dock(); +// assert!(left_dock.read(cx).is_open()); +// assert_eq!( +// left_dock.read(cx).visible_panel().unwrap().id(), +// panel_1.id() +// ); +// }); + +// // Emitting a ZoomIn event shows the panel as zoomed. +// panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn)); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); +// assert_eq!(workspace.zoomed_position, Some(DockPosition::Left)); +// }); + +// // Move panel to another dock while it is zoomed +// panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx)); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); +// assert_eq!(workspace.zoomed_position, Some(DockPosition::Right)); +// }); + +// // If focus is transferred to another view that's not a panel or another pane, we still show +// // the panel as zoomed. +// let focus_receiver = window.add_view(cx, |_| EmptyView); +// focus_receiver.update(cx, |_, cx| cx.focus_self()); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); +// assert_eq!(workspace.zoomed_position, Some(DockPosition::Right)); +// }); + +// // If focus is transferred elsewhere in the workspace, the panel is no longer zoomed. +// workspace.update(cx, |_, cx| cx.focus_self()); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, None); +// assert_eq!(workspace.zoomed_position, None); +// }); + +// // If focus is transferred again to another view that's not a panel or a pane, we won't +// // show the panel as zoomed because it wasn't zoomed before. +// focus_receiver.update(cx, |_, cx| cx.focus_self()); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, None); +// assert_eq!(workspace.zoomed_position, None); +// }); + +// // When focus is transferred back to the panel, it is zoomed again. +// panel_1.update(cx, |_, cx| cx.focus_self()); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); +// assert_eq!(workspace.zoomed_position, Some(DockPosition::Right)); +// }); + +// // Emitting a ZoomOut event unzooms the panel. +// panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut)); +// workspace.read_with(cx, |workspace, _| { +// assert_eq!(workspace.zoomed, None); +// assert_eq!(workspace.zoomed_position, None); +// }); + +// // Emit closed event on panel 1, which is active +// panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::Closed)); + +// // Now the left dock is closed, because panel_1 was the active panel +// workspace.read_with(cx, |workspace, cx| { +// let right_dock = workspace.right_dock(); +// assert!(!right_dock.read(cx).is_open()); +// }); +// } + +// pub fn init_test(cx: &mut TestAppContext) { +// cx.foreground().forbid_parking(); +// cx.update(|cx| { +// cx.set_global(SettingsStore::test(cx)); +// theme::init((), cx); +// language::init(cx); +// crate::init_settings(cx); +// Project::init_settings(cx); +// }); +// } +// } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index b9968a3ef5..a4270856b8 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -120,7 +120,7 @@ fn main() { let node_runtime = RealNodeRuntime::new(http.clone()); language2::init(cx); - let user_store = cx.entity(|cx| UserStore::new(client.clone(), http.clone(), cx)); + let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); // let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx)); cx.set_global(client.clone()); diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index d78908dfb5..45a9276b2b 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -4,7 +4,7 @@ mod open_listener; pub use assets::*; use client2::{Client, UserStore}; -use gpui2::{AsyncAppContext, Handle}; +use gpui2::{AsyncAppContext, Model}; pub use only_instance::*; pub use open_listener::*; @@ -47,7 +47,7 @@ pub fn connect_to_cli( pub struct AppState { pub client: Arc, - pub user_store: Handle, + pub user_store: Model, } pub async fn handle_cli_connection( From ba789fc0c4863c80f501aa1569dcfc7e8e6d3f61 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 14:47:38 -0400 Subject: [PATCH 36/54] Remove old theme constructs --- crates/storybook2/src/stories/focus.rs | 16 +- crates/storybook2/src/stories/scroll.rs | 18 +- crates/storybook2/src/storybook2.rs | 2 - crates/storybook2/src/themes.rs | 30 - crates/storybook2/src/themes/rose_pine.rs | 1686 --------------------- crates/theme2/src/theme2.rs | 4 + crates/ui2/src/lib.rs | 2 - crates/ui2/src/prelude.rs | 3 +- crates/ui2/src/theme.rs | 134 -- 9 files changed, 21 insertions(+), 1874 deletions(-) delete mode 100644 crates/storybook2/src/themes.rs delete mode 100644 crates/storybook2/src/themes/rose_pine.rs delete mode 100644 crates/ui2/src/theme.rs diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index aeb0a243b2..b1db0187d6 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/crates/storybook2/src/stories/focus.rs @@ -1,9 +1,9 @@ -use crate::themes::rose_pine; use gpui2::{ div, Focusable, KeyBinding, ParentElement, StatelessInteractive, Styled, View, VisualContext, WindowContext, }; use serde::Deserialize; +use theme2::theme; #[derive(Clone, Default, PartialEq, Deserialize)] struct ActionA; @@ -27,14 +27,14 @@ impl FocusStory { ]); cx.register_action_type::(); cx.register_action_type::(); - let theme = rose_pine(); + let theme = theme(cx); - let color_1 = theme.lowest.negative.default.foreground; - let color_2 = theme.lowest.positive.default.foreground; - let color_3 = theme.lowest.warning.default.foreground; - let color_4 = theme.lowest.accent.default.foreground; - let color_5 = theme.lowest.variant.default.foreground; - let color_6 = theme.highest.negative.default.foreground; + let color_1 = theme.git_created; + let color_2 = theme.git_modified; + let color_3 = theme.git_deleted; + let color_4 = theme.git_conflict; + let color_5 = theme.git_ignored; + let color_6 = theme.git_renamed; let child_1 = cx.focus_handle(); let child_2 = cx.focus_handle(); diff --git a/crates/storybook2/src/stories/scroll.rs b/crates/storybook2/src/stories/scroll.rs index 662d44328b..1b8877ef5c 100644 --- a/crates/storybook2/src/stories/scroll.rs +++ b/crates/storybook2/src/stories/scroll.rs @@ -1,7 +1,7 @@ -use crate::themes::rose_pine; use gpui2::{ div, px, Component, ParentElement, SharedString, Styled, View, VisualContext, WindowContext, }; +use theme2::theme; pub struct ScrollStory { text: View<()>, @@ -9,25 +9,21 @@ pub struct ScrollStory { impl ScrollStory { pub fn view(cx: &mut WindowContext) -> View<()> { - let theme = rose_pine(); - - { - cx.build_view(|cx| (), move |_, cx| checkerboard(1)) - } + cx.build_view(|cx| (), move |_, cx| checkerboard(cx, 1)) } } -fn checkerboard(depth: usize) -> impl Component +fn checkerboard(cx: &mut WindowContext, depth: usize) -> impl Component where S: 'static + Send + Sync, { - let theme = rose_pine(); - let color_1 = theme.lowest.positive.default.background; - let color_2 = theme.lowest.warning.default.background; + let theme = theme(cx); + let color_1 = theme.git_created; + let color_2 = theme.git_modified; div() .id("parent") - .bg(theme.lowest.base.default.background) + .bg(theme.background) .size_full() .overflow_scroll() .children((0..10).map(|row| { diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 493997ccfe..584a9abe39 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -4,7 +4,6 @@ mod assets; mod stories; mod story; mod story_selector; -mod themes; use std::sync::Arc; @@ -50,7 +49,6 @@ fn main() { let story_selector = args.story.clone(); let theme_name = args.theme.unwrap_or("One Dark".to_string()); - let theme = themes::load_theme(theme_name.clone()).unwrap(); let asset_source = Arc::new(Assets); gpui2::App::production(asset_source).run(move |cx| { diff --git a/crates/storybook2/src/themes.rs b/crates/storybook2/src/themes.rs deleted file mode 100644 index ade7cbea87..0000000000 --- a/crates/storybook2/src/themes.rs +++ /dev/null @@ -1,30 +0,0 @@ -mod rose_pine; - -pub use rose_pine::*; - -use anyhow::{Context, Result}; -use gpui2::serde_json; -use serde::Deserialize; -use ui::Theme; - -use crate::assets::Assets; - -#[derive(Deserialize)] -struct LegacyTheme { - pub base_theme: serde_json::Value, -} - -/// Loads the [`Theme`] with the given name. -pub fn load_theme(name: String) -> Result { - let theme_contents = Assets::get(&format!("themes/{name}.json")) - .with_context(|| format!("theme file not found: '{name}'"))?; - - let legacy_theme: LegacyTheme = - serde_json::from_str(std::str::from_utf8(&theme_contents.data)?) - .context("failed to parse legacy theme")?; - - let theme: Theme = serde_json::from_value(legacy_theme.base_theme.clone()) - .context("failed to parse `base_theme`")?; - - Ok(theme) -} diff --git a/crates/storybook2/src/themes/rose_pine.rs b/crates/storybook2/src/themes/rose_pine.rs deleted file mode 100644 index a553f87d77..0000000000 --- a/crates/storybook2/src/themes/rose_pine.rs +++ /dev/null @@ -1,1686 +0,0 @@ -use gpui2::serde_json::{self, json}; -use ui::Theme; - -pub fn rose_pine() -> Theme { - serde_json::from_value(json! { - { - "name": "Rosé Pine", - "is_light": false, - "ramps": {}, - "lowest": { - "base": { - "default": { - "background": "#292739", - "border": "#423f55", - "foreground": "#e0def4" - }, - "hovered": { - "background": "#423f55", - "border": "#423f55", - "foreground": "#e0def4" - }, - "pressed": { - "background": "#4e4b63", - "border": "#423f55", - "foreground": "#e0def4" - }, - "active": { - "background": "#47445b", - "border": "#36334a", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#292739", - "border": "#353347", - "foreground": "#2f2b43" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#4b4860" - } - }, - "variant": { - "default": { - "background": "#292739", - "border": "#423f55", - "foreground": "#75718e" - }, - "hovered": { - "background": "#423f55", - "border": "#423f55", - "foreground": "#75718e" - }, - "pressed": { - "background": "#4e4b63", - "border": "#423f55", - "foreground": "#75718e" - }, - "active": { - "background": "#47445b", - "border": "#36334a", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#292739", - "border": "#353347", - "foreground": "#2f2b43" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#4b4860" - } - }, - "on": { - "default": { - "background": "#1d1b2a", - "border": "#232132", - "foreground": "#e0def4" - }, - "hovered": { - "background": "#232132", - "border": "#232132", - "foreground": "#e0def4" - }, - "pressed": { - "background": "#2f2d40", - "border": "#232132", - "foreground": "#e0def4" - }, - "active": { - "background": "#403e53", - "border": "#504d65", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#1d1b2a", - "border": "#1e1c2c", - "foreground": "#3b384f" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#3b394e" - } - }, - "accent": { - "default": { - "background": "#2f3739", - "border": "#435255", - "foreground": "#9cced7" - }, - "hovered": { - "background": "#435255", - "border": "#435255", - "foreground": "#9cced7" - }, - "pressed": { - "background": "#4e6164", - "border": "#435255", - "foreground": "#9cced7" - }, - "active": { - "background": "#5d757a", - "border": "#6e8f94", - "foreground": "#fbfdfd" - }, - "disabled": { - "background": "#2f3739", - "border": "#3a4446", - "foreground": "#85aeb5" - }, - "inverted": { - "background": "#fbfdfd", - "border": "#171717", - "foreground": "#587074" - } - }, - "positive": { - "default": { - "background": "#182e23", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "hovered": { - "background": "#254839", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "pressed": { - "background": "#2c5645", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "active": { - "background": "#356b57", - "border": "#40836c", - "foreground": "#f9fdfb" - }, - "disabled": { - "background": "#182e23", - "border": "#1e3b2e", - "foreground": "#4ea287" - }, - "inverted": { - "background": "#f9fdfb", - "border": "#000e00", - "foreground": "#326552" - } - }, - "warning": { - "default": { - "background": "#50341a", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "hovered": { - "background": "#6d4d2b", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "pressed": { - "background": "#7e5a34", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "active": { - "background": "#946e41", - "border": "#b0854f", - "foreground": "#fffcf9" - }, - "disabled": { - "background": "#50341a", - "border": "#5e4023", - "foreground": "#d2a263" - }, - "inverted": { - "background": "#fffcf9", - "border": "#2c1600", - "foreground": "#8e683c" - } - }, - "negative": { - "default": { - "background": "#431820", - "border": "#612834", - "foreground": "#ea6f92" - }, - "hovered": { - "background": "#612834", - "border": "#612834", - "foreground": "#ea6f92" - }, - "pressed": { - "background": "#71303f", - "border": "#612834", - "foreground": "#ea6f92" - }, - "active": { - "background": "#883c4f", - "border": "#a44961", - "foreground": "#fff9fa" - }, - "disabled": { - "background": "#431820", - "border": "#52202a", - "foreground": "#c75c79" - }, - "inverted": { - "background": "#fff9fa", - "border": "#230000", - "foreground": "#82384a" - } - } - }, - "middle": { - "base": { - "default": { - "background": "#1d1b2a", - "border": "#232132", - "foreground": "#e0def4" - }, - "hovered": { - "background": "#232132", - "border": "#232132", - "foreground": "#e0def4" - }, - "pressed": { - "background": "#2f2d40", - "border": "#232132", - "foreground": "#e0def4" - }, - "active": { - "background": "#403e53", - "border": "#504d65", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#1d1b2a", - "border": "#1e1c2c", - "foreground": "#3b384f" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#3b394e" - } - }, - "variant": { - "default": { - "background": "#1d1b2a", - "border": "#232132", - "foreground": "#75718e" - }, - "hovered": { - "background": "#232132", - "border": "#232132", - "foreground": "#75718e" - }, - "pressed": { - "background": "#2f2d40", - "border": "#232132", - "foreground": "#75718e" - }, - "active": { - "background": "#403e53", - "border": "#504d65", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#1d1b2a", - "border": "#1e1c2c", - "foreground": "#3b384f" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#3b394e" - } - }, - "on": { - "default": { - "background": "#191724", - "border": "#1c1a29", - "foreground": "#e0def4" - }, - "hovered": { - "background": "#1c1a29", - "border": "#1c1a29", - "foreground": "#e0def4" - }, - "pressed": { - "background": "#1d1b2b", - "border": "#1c1a29", - "foreground": "#e0def4" - }, - "active": { - "background": "#222031", - "border": "#353347", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#191724", - "border": "#1a1826", - "foreground": "#4e4b63" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#1f1d2e" - } - }, - "accent": { - "default": { - "background": "#2f3739", - "border": "#435255", - "foreground": "#9cced7" - }, - "hovered": { - "background": "#435255", - "border": "#435255", - "foreground": "#9cced7" - }, - "pressed": { - "background": "#4e6164", - "border": "#435255", - "foreground": "#9cced7" - }, - "active": { - "background": "#5d757a", - "border": "#6e8f94", - "foreground": "#fbfdfd" - }, - "disabled": { - "background": "#2f3739", - "border": "#3a4446", - "foreground": "#85aeb5" - }, - "inverted": { - "background": "#fbfdfd", - "border": "#171717", - "foreground": "#587074" - } - }, - "positive": { - "default": { - "background": "#182e23", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "hovered": { - "background": "#254839", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "pressed": { - "background": "#2c5645", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "active": { - "background": "#356b57", - "border": "#40836c", - "foreground": "#f9fdfb" - }, - "disabled": { - "background": "#182e23", - "border": "#1e3b2e", - "foreground": "#4ea287" - }, - "inverted": { - "background": "#f9fdfb", - "border": "#000e00", - "foreground": "#326552" - } - }, - "warning": { - "default": { - "background": "#50341a", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "hovered": { - "background": "#6d4d2b", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "pressed": { - "background": "#7e5a34", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "active": { - "background": "#946e41", - "border": "#b0854f", - "foreground": "#fffcf9" - }, - "disabled": { - "background": "#50341a", - "border": "#5e4023", - "foreground": "#d2a263" - }, - "inverted": { - "background": "#fffcf9", - "border": "#2c1600", - "foreground": "#8e683c" - } - }, - "negative": { - "default": { - "background": "#431820", - "border": "#612834", - "foreground": "#ea6f92" - }, - "hovered": { - "background": "#612834", - "border": "#612834", - "foreground": "#ea6f92" - }, - "pressed": { - "background": "#71303f", - "border": "#612834", - "foreground": "#ea6f92" - }, - "active": { - "background": "#883c4f", - "border": "#a44961", - "foreground": "#fff9fa" - }, - "disabled": { - "background": "#431820", - "border": "#52202a", - "foreground": "#c75c79" - }, - "inverted": { - "background": "#fff9fa", - "border": "#230000", - "foreground": "#82384a" - } - } - }, - "highest": { - "base": { - "default": { - "background": "#191724", - "border": "#1c1a29", - "foreground": "#e0def4" - }, - "hovered": { - "background": "#1c1a29", - "border": "#1c1a29", - "foreground": "#e0def4" - }, - "pressed": { - "background": "#1d1b2b", - "border": "#1c1a29", - "foreground": "#e0def4" - }, - "active": { - "background": "#222031", - "border": "#353347", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#191724", - "border": "#1a1826", - "foreground": "#4e4b63" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#1f1d2e" - } - }, - "variant": { - "default": { - "background": "#191724", - "border": "#1c1a29", - "foreground": "#75718e" - }, - "hovered": { - "background": "#1c1a29", - "border": "#1c1a29", - "foreground": "#75718e" - }, - "pressed": { - "background": "#1d1b2b", - "border": "#1c1a29", - "foreground": "#75718e" - }, - "active": { - "background": "#222031", - "border": "#353347", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#191724", - "border": "#1a1826", - "foreground": "#4e4b63" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#1f1d2e" - } - }, - "on": { - "default": { - "background": "#1d1b2a", - "border": "#232132", - "foreground": "#e0def4" - }, - "hovered": { - "background": "#232132", - "border": "#232132", - "foreground": "#e0def4" - }, - "pressed": { - "background": "#2f2d40", - "border": "#232132", - "foreground": "#e0def4" - }, - "active": { - "background": "#403e53", - "border": "#504d65", - "foreground": "#e0def4" - }, - "disabled": { - "background": "#1d1b2a", - "border": "#1e1c2c", - "foreground": "#3b384f" - }, - "inverted": { - "background": "#e0def4", - "border": "#191724", - "foreground": "#3b394e" - } - }, - "accent": { - "default": { - "background": "#2f3739", - "border": "#435255", - "foreground": "#9cced7" - }, - "hovered": { - "background": "#435255", - "border": "#435255", - "foreground": "#9cced7" - }, - "pressed": { - "background": "#4e6164", - "border": "#435255", - "foreground": "#9cced7" - }, - "active": { - "background": "#5d757a", - "border": "#6e8f94", - "foreground": "#fbfdfd" - }, - "disabled": { - "background": "#2f3739", - "border": "#3a4446", - "foreground": "#85aeb5" - }, - "inverted": { - "background": "#fbfdfd", - "border": "#171717", - "foreground": "#587074" - } - }, - "positive": { - "default": { - "background": "#182e23", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "hovered": { - "background": "#254839", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "pressed": { - "background": "#2c5645", - "border": "#254839", - "foreground": "#5dc2a3" - }, - "active": { - "background": "#356b57", - "border": "#40836c", - "foreground": "#f9fdfb" - }, - "disabled": { - "background": "#182e23", - "border": "#1e3b2e", - "foreground": "#4ea287" - }, - "inverted": { - "background": "#f9fdfb", - "border": "#000e00", - "foreground": "#326552" - } - }, - "warning": { - "default": { - "background": "#50341a", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "hovered": { - "background": "#6d4d2b", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "pressed": { - "background": "#7e5a34", - "border": "#6d4d2b", - "foreground": "#f5c177" - }, - "active": { - "background": "#946e41", - "border": "#b0854f", - "foreground": "#fffcf9" - }, - "disabled": { - "background": "#50341a", - "border": "#5e4023", - "foreground": "#d2a263" - }, - "inverted": { - "background": "#fffcf9", - "border": "#2c1600", - "foreground": "#8e683c" - } - }, - "negative": { - "default": { - "background": "#431820", - "border": "#612834", - "foreground": "#ea6f92" - }, - "hovered": { - "background": "#612834", - "border": "#612834", - "foreground": "#ea6f92" - }, - "pressed": { - "background": "#71303f", - "border": "#612834", - "foreground": "#ea6f92" - }, - "active": { - "background": "#883c4f", - "border": "#a44961", - "foreground": "#fff9fa" - }, - "disabled": { - "background": "#431820", - "border": "#52202a", - "foreground": "#c75c79" - }, - "inverted": { - "background": "#fff9fa", - "border": "#230000", - "foreground": "#82384a" - } - } - }, - "popover_shadow": { - "blur": 4, - "color": "#00000033", - "offset": [ - 1, - 2 - ] - }, - "modal_shadow": { - "blur": 16, - "color": "#00000033", - "offset": [ - 0, - 2 - ] - }, - "players": { - "0": { - "selection": "#9cced73d", - "cursor": "#9cced7" - }, - "1": { - "selection": "#5dc2a33d", - "cursor": "#5dc2a3" - }, - "2": { - "selection": "#9d76913d", - "cursor": "#9d7691" - }, - "3": { - "selection": "#c4a7e63d", - "cursor": "#c4a7e6" - }, - "4": { - "selection": "#c4a7e63d", - "cursor": "#c4a7e6" - }, - "5": { - "selection": "#32748f3d", - "cursor": "#32748f" - }, - "6": { - "selection": "#ea6f923d", - "cursor": "#ea6f92" - }, - "7": { - "selection": "#f5c1773d", - "cursor": "#f5c177" - } - }, - "syntax": { - "comment": { - "color": "#6e6a86" - }, - "operator": { - "color": "#31748f" - }, - "punctuation": { - "color": "#908caa" - }, - "variable": { - "color": "#e0def4" - }, - "string": { - "color": "#f6c177" - }, - "type": { - "color": "#9ccfd8" - }, - "type.builtin": { - "color": "#9ccfd8" - }, - "boolean": { - "color": "#ebbcba" - }, - "function": { - "color": "#ebbcba" - }, - "keyword": { - "color": "#31748f" - }, - "tag": { - "color": "#9ccfd8" - }, - "function.method": { - "color": "#ebbcba" - }, - "title": { - "color": "#f6c177" - }, - "link_text": { - "color": "#9ccfd8", - "italic": false - }, - "link_uri": { - "color": "#ebbcba" - } - }, - "color_family": { - "neutral": { - "low": 11.568627450980392, - "high": 91.37254901960785, - "range": 79.80392156862746, - "scaling_value": 1.2530712530712529 - }, - "red": { - "low": 6.862745098039216, - "high": 100, - "range": 93.13725490196079, - "scaling_value": 1.0736842105263158 - }, - "orange": { - "low": 5.490196078431373, - "high": 100, - "range": 94.50980392156863, - "scaling_value": 1.058091286307054 - }, - "yellow": { - "low": 8.627450980392156, - "high": 100, - "range": 91.37254901960785, - "scaling_value": 1.094420600858369 - }, - "green": { - "low": 2.7450980392156863, - "high": 100, - "range": 97.25490196078431, - "scaling_value": 1.028225806451613 - }, - "cyan": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "blue": { - "low": 9.019607843137255, - "high": 100, - "range": 90.98039215686275, - "scaling_value": 1.0991379310344827 - }, - "violet": { - "low": 5.490196078431373, - "high": 100, - "range": 94.50980392156863, - "scaling_value": 1.058091286307054 - }, - "magenta": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - } - } - } - }) - .unwrap() -} - -pub fn rose_pine_dawn() -> Theme { - serde_json::from_value(json!({ - "name": "Rosé Pine Dawn", - "is_light": true, - "ramps": {}, - "lowest": { - "base": { - "default": { - "background": "#dcd8d8", - "border": "#dcd6d5", - "foreground": "#575279" - }, - "hovered": { - "background": "#dcd6d5", - "border": "#dcd6d5", - "foreground": "#575279" - }, - "pressed": { - "background": "#efe6df", - "border": "#dcd6d5", - "foreground": "#575279" - }, - "active": { - "background": "#c1bac1", - "border": "#a9a3b0", - "foreground": "#575279" - }, - "disabled": { - "background": "#dcd8d8", - "border": "#d0cccf", - "foreground": "#938fa3" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#c7c0c5" - } - }, - "variant": { - "default": { - "background": "#dcd8d8", - "border": "#dcd6d5", - "foreground": "#706c8c" - }, - "hovered": { - "background": "#dcd6d5", - "border": "#dcd6d5", - "foreground": "#706c8c" - }, - "pressed": { - "background": "#efe6df", - "border": "#dcd6d5", - "foreground": "#706c8c" - }, - "active": { - "background": "#c1bac1", - "border": "#a9a3b0", - "foreground": "#575279" - }, - "disabled": { - "background": "#dcd8d8", - "border": "#d0cccf", - "foreground": "#938fa3" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#c7c0c5" - } - }, - "on": { - "default": { - "background": "#fef9f2", - "border": "#e5e0df", - "foreground": "#575279" - }, - "hovered": { - "background": "#e5e0df", - "border": "#e5e0df", - "foreground": "#575279" - }, - "pressed": { - "background": "#d4d0d2", - "border": "#e5e0df", - "foreground": "#575279" - }, - "active": { - "background": "#dbd5d4", - "border": "#dbd3d1", - "foreground": "#575279" - }, - "disabled": { - "background": "#fef9f2", - "border": "#f6f1eb", - "foreground": "#b1abb5" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#d6d1d1" - } - }, - "accent": { - "default": { - "background": "#dde9eb", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "hovered": { - "background": "#c3d7db", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "pressed": { - "background": "#b6cfd3", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "active": { - "background": "#a3c3c9", - "border": "#8db6bd", - "foreground": "#06090a" - }, - "disabled": { - "background": "#dde9eb", - "border": "#d0e0e3", - "foreground": "#72a5ae" - }, - "inverted": { - "background": "#06090a", - "border": "#ffffff", - "foreground": "#a8c7cd" - } - }, - "positive": { - "default": { - "background": "#dbeee7", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "hovered": { - "background": "#bee0d5", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "pressed": { - "background": "#b0dacb", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "active": { - "background": "#9bd0bf", - "border": "#82c6b1", - "foreground": "#060a09" - }, - "disabled": { - "background": "#dbeee7", - "border": "#cde7de", - "foreground": "#63b89f" - }, - "inverted": { - "background": "#060a09", - "border": "#ffffff", - "foreground": "#a1d4c3" - } - }, - "warning": { - "default": { - "background": "#ffebd6", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "hovered": { - "background": "#ffdab7", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "pressed": { - "background": "#fed2a6", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "active": { - "background": "#fbc891", - "border": "#f7bc77", - "foreground": "#330704" - }, - "disabled": { - "background": "#ffebd6", - "border": "#ffe2c7", - "foreground": "#f1ac57" - }, - "inverted": { - "background": "#330704", - "border": "#ffffff", - "foreground": "#fccb97" - } - }, - "negative": { - "default": { - "background": "#f1dfe3", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "hovered": { - "background": "#e6c6cd", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "pressed": { - "background": "#e0bac2", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "active": { - "background": "#d8a8b3", - "border": "#ce94a3", - "foreground": "#0b0708" - }, - "disabled": { - "background": "#f1dfe3", - "border": "#ecd2d8", - "foreground": "#c17b8e" - }, - "inverted": { - "background": "#0b0708", - "border": "#ffffff", - "foreground": "#dbadb8" - } - } - }, - "middle": { - "base": { - "default": { - "background": "#fef9f2", - "border": "#e5e0df", - "foreground": "#575279" - }, - "hovered": { - "background": "#e5e0df", - "border": "#e5e0df", - "foreground": "#575279" - }, - "pressed": { - "background": "#d4d0d2", - "border": "#e5e0df", - "foreground": "#575279" - }, - "active": { - "background": "#dbd5d4", - "border": "#dbd3d1", - "foreground": "#575279" - }, - "disabled": { - "background": "#fef9f2", - "border": "#f6f1eb", - "foreground": "#b1abb5" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#d6d1d1" - } - }, - "variant": { - "default": { - "background": "#fef9f2", - "border": "#e5e0df", - "foreground": "#706c8c" - }, - "hovered": { - "background": "#e5e0df", - "border": "#e5e0df", - "foreground": "#706c8c" - }, - "pressed": { - "background": "#d4d0d2", - "border": "#e5e0df", - "foreground": "#706c8c" - }, - "active": { - "background": "#dbd5d4", - "border": "#dbd3d1", - "foreground": "#575279" - }, - "disabled": { - "background": "#fef9f2", - "border": "#f6f1eb", - "foreground": "#b1abb5" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#d6d1d1" - } - }, - "on": { - "default": { - "background": "#faf4ed", - "border": "#fdf8f1", - "foreground": "#575279" - }, - "hovered": { - "background": "#fdf8f1", - "border": "#fdf8f1", - "foreground": "#575279" - }, - "pressed": { - "background": "#fdf8f2", - "border": "#fdf8f1", - "foreground": "#575279" - }, - "active": { - "background": "#e6e1e0", - "border": "#d0cccf", - "foreground": "#575279" - }, - "disabled": { - "background": "#faf4ed", - "border": "#fcf6ef", - "foreground": "#efe6df" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#ede9e5" - } - }, - "accent": { - "default": { - "background": "#dde9eb", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "hovered": { - "background": "#c3d7db", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "pressed": { - "background": "#b6cfd3", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "active": { - "background": "#a3c3c9", - "border": "#8db6bd", - "foreground": "#06090a" - }, - "disabled": { - "background": "#dde9eb", - "border": "#d0e0e3", - "foreground": "#72a5ae" - }, - "inverted": { - "background": "#06090a", - "border": "#ffffff", - "foreground": "#a8c7cd" - } - }, - "positive": { - "default": { - "background": "#dbeee7", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "hovered": { - "background": "#bee0d5", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "pressed": { - "background": "#b0dacb", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "active": { - "background": "#9bd0bf", - "border": "#82c6b1", - "foreground": "#060a09" - }, - "disabled": { - "background": "#dbeee7", - "border": "#cde7de", - "foreground": "#63b89f" - }, - "inverted": { - "background": "#060a09", - "border": "#ffffff", - "foreground": "#a1d4c3" - } - }, - "warning": { - "default": { - "background": "#ffebd6", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "hovered": { - "background": "#ffdab7", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "pressed": { - "background": "#fed2a6", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "active": { - "background": "#fbc891", - "border": "#f7bc77", - "foreground": "#330704" - }, - "disabled": { - "background": "#ffebd6", - "border": "#ffe2c7", - "foreground": "#f1ac57" - }, - "inverted": { - "background": "#330704", - "border": "#ffffff", - "foreground": "#fccb97" - } - }, - "negative": { - "default": { - "background": "#f1dfe3", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "hovered": { - "background": "#e6c6cd", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "pressed": { - "background": "#e0bac2", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "active": { - "background": "#d8a8b3", - "border": "#ce94a3", - "foreground": "#0b0708" - }, - "disabled": { - "background": "#f1dfe3", - "border": "#ecd2d8", - "foreground": "#c17b8e" - }, - "inverted": { - "background": "#0b0708", - "border": "#ffffff", - "foreground": "#dbadb8" - } - } - }, - "highest": { - "base": { - "default": { - "background": "#faf4ed", - "border": "#fdf8f1", - "foreground": "#575279" - }, - "hovered": { - "background": "#fdf8f1", - "border": "#fdf8f1", - "foreground": "#575279" - }, - "pressed": { - "background": "#fdf8f2", - "border": "#fdf8f1", - "foreground": "#575279" - }, - "active": { - "background": "#e6e1e0", - "border": "#d0cccf", - "foreground": "#575279" - }, - "disabled": { - "background": "#faf4ed", - "border": "#fcf6ef", - "foreground": "#efe6df" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#ede9e5" - } - }, - "variant": { - "default": { - "background": "#faf4ed", - "border": "#fdf8f1", - "foreground": "#706c8c" - }, - "hovered": { - "background": "#fdf8f1", - "border": "#fdf8f1", - "foreground": "#706c8c" - }, - "pressed": { - "background": "#fdf8f2", - "border": "#fdf8f1", - "foreground": "#706c8c" - }, - "active": { - "background": "#e6e1e0", - "border": "#d0cccf", - "foreground": "#575279" - }, - "disabled": { - "background": "#faf4ed", - "border": "#fcf6ef", - "foreground": "#efe6df" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#ede9e5" - } - }, - "on": { - "default": { - "background": "#fef9f2", - "border": "#e5e0df", - "foreground": "#575279" - }, - "hovered": { - "background": "#e5e0df", - "border": "#e5e0df", - "foreground": "#575279" - }, - "pressed": { - "background": "#d4d0d2", - "border": "#e5e0df", - "foreground": "#575279" - }, - "active": { - "background": "#dbd5d4", - "border": "#dbd3d1", - "foreground": "#575279" - }, - "disabled": { - "background": "#fef9f2", - "border": "#f6f1eb", - "foreground": "#b1abb5" - }, - "inverted": { - "background": "#575279", - "border": "#faf4ed", - "foreground": "#d6d1d1" - } - }, - "accent": { - "default": { - "background": "#dde9eb", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "hovered": { - "background": "#c3d7db", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "pressed": { - "background": "#b6cfd3", - "border": "#c3d7db", - "foreground": "#57949f" - }, - "active": { - "background": "#a3c3c9", - "border": "#8db6bd", - "foreground": "#06090a" - }, - "disabled": { - "background": "#dde9eb", - "border": "#d0e0e3", - "foreground": "#72a5ae" - }, - "inverted": { - "background": "#06090a", - "border": "#ffffff", - "foreground": "#a8c7cd" - } - }, - "positive": { - "default": { - "background": "#dbeee7", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "hovered": { - "background": "#bee0d5", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "pressed": { - "background": "#b0dacb", - "border": "#bee0d5", - "foreground": "#3eaa8e" - }, - "active": { - "background": "#9bd0bf", - "border": "#82c6b1", - "foreground": "#060a09" - }, - "disabled": { - "background": "#dbeee7", - "border": "#cde7de", - "foreground": "#63b89f" - }, - "inverted": { - "background": "#060a09", - "border": "#ffffff", - "foreground": "#a1d4c3" - } - }, - "warning": { - "default": { - "background": "#ffebd6", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "hovered": { - "background": "#ffdab7", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "pressed": { - "background": "#fed2a6", - "border": "#ffdab7", - "foreground": "#e99d35" - }, - "active": { - "background": "#fbc891", - "border": "#f7bc77", - "foreground": "#330704" - }, - "disabled": { - "background": "#ffebd6", - "border": "#ffe2c7", - "foreground": "#f1ac57" - }, - "inverted": { - "background": "#330704", - "border": "#ffffff", - "foreground": "#fccb97" - } - }, - "negative": { - "default": { - "background": "#f1dfe3", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "hovered": { - "background": "#e6c6cd", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "pressed": { - "background": "#e0bac2", - "border": "#e6c6cd", - "foreground": "#b4647a" - }, - "active": { - "background": "#d8a8b3", - "border": "#ce94a3", - "foreground": "#0b0708" - }, - "disabled": { - "background": "#f1dfe3", - "border": "#ecd2d8", - "foreground": "#c17b8e" - }, - "inverted": { - "background": "#0b0708", - "border": "#ffffff", - "foreground": "#dbadb8" - } - } - }, - "popover_shadow": { - "blur": 4, - "color": "#2c2a4d33", - "offset": [ - 1, - 2 - ] - }, - "modal_shadow": { - "blur": 16, - "color": "#2c2a4d33", - "offset": [ - 0, - 2 - ] - }, - "players": { - "0": { - "selection": "#57949f3d", - "cursor": "#57949f" - }, - "1": { - "selection": "#3eaa8e3d", - "cursor": "#3eaa8e" - }, - "2": { - "selection": "#7c697f3d", - "cursor": "#7c697f" - }, - "3": { - "selection": "#907aa93d", - "cursor": "#907aa9" - }, - "4": { - "selection": "#907aa93d", - "cursor": "#907aa9" - }, - "5": { - "selection": "#2a69833d", - "cursor": "#2a6983" - }, - "6": { - "selection": "#b4647a3d", - "cursor": "#b4647a" - }, - "7": { - "selection": "#e99d353d", - "cursor": "#e99d35" - } - }, - "syntax": { - "comment": { - "color": "#9893a5" - }, - "operator": { - "color": "#286983" - }, - "punctuation": { - "color": "#797593" - }, - "variable": { - "color": "#575279" - }, - "string": { - "color": "#ea9d34" - }, - "type": { - "color": "#56949f" - }, - "type.builtin": { - "color": "#56949f" - }, - "boolean": { - "color": "#d7827e" - }, - "function": { - "color": "#d7827e" - }, - "keyword": { - "color": "#286983" - }, - "tag": { - "color": "#56949f" - }, - "function.method": { - "color": "#d7827e" - }, - "title": { - "color": "#ea9d34" - }, - "link_text": { - "color": "#56949f", - "italic": false - }, - "link_uri": { - "color": "#d7827e" - } - }, - "color_family": { - "neutral": { - "low": 39.80392156862745, - "high": 95.49019607843137, - "range": 55.686274509803916, - "scaling_value": 1.7957746478873242 - }, - "red": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "orange": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "yellow": { - "low": 8.823529411764707, - "high": 100, - "range": 91.17647058823529, - "scaling_value": 1.0967741935483872 - }, - "green": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "cyan": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "blue": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "violet": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - }, - "magenta": { - "low": 0, - "high": 100, - "range": 100, - "scaling_value": 1 - } - } - })) - .unwrap() -} diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 9a7d58a6c7..6d4d5269e3 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -18,6 +18,10 @@ pub fn active_theme<'a>(cx: &'a AppContext) -> &'a Arc { &ThemeSettings::get_global(cx).active_theme } +pub fn theme(cx: &AppContext) -> Arc { + active_theme(cx).clone() +} + pub struct Theme { pub metadata: ThemeMetadata, diff --git a/crates/ui2/src/lib.rs b/crates/ui2/src/lib.rs index 689e9c5372..c1da5e410d 100644 --- a/crates/ui2/src/lib.rs +++ b/crates/ui2/src/lib.rs @@ -23,7 +23,6 @@ mod elevation; pub mod prelude; pub mod settings; mod static_data; -mod theme; pub use components::*; pub use elements::*; @@ -38,7 +37,6 @@ pub use static_data::*; // AFAICT this is something to do with conflicting names between crates and modules that // interfaces with declaring the `ClassDecl`. pub use crate::settings::*; -pub use crate::theme::*; #[cfg(feature = "stories")] mod story; diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 5d701fc5d7..63405fc2cb 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -5,7 +5,8 @@ pub use gpui2::{ pub use crate::elevation::*; use crate::settings::user_settings; -pub use crate::{theme, ButtonVariant}; +pub use crate::ButtonVariant; +pub use theme2::theme; use gpui2::{rems, Hsla, Rems}; use strum::EnumIter; diff --git a/crates/ui2/src/theme.rs b/crates/ui2/src/theme.rs deleted file mode 100644 index 98c93f9bdd..0000000000 --- a/crates/ui2/src/theme.rs +++ /dev/null @@ -1,134 +0,0 @@ -use gpui2::{AppContext, Hsla, Result}; -use serde::{de::Visitor, Deserialize, Deserializer}; -use std::collections::HashMap; -use std::fmt; -use std::sync::Arc; - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct Theme { - pub name: String, - pub is_light: bool, - pub lowest: Layer, - pub middle: Layer, - pub highest: Layer, - pub popover_shadow: Shadow, - pub modal_shadow: Shadow, - #[serde(deserialize_with = "deserialize_player_colors")] - pub players: Vec, - #[serde(deserialize_with = "deserialize_syntax_colors")] - pub syntax: HashMap, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct Layer { - pub base: StyleSet, - pub variant: StyleSet, - pub on: StyleSet, - pub accent: StyleSet, - pub positive: StyleSet, - pub warning: StyleSet, - pub negative: StyleSet, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct StyleSet { - #[serde(rename = "default")] - pub default: ContainerColors, - pub hovered: ContainerColors, - pub pressed: ContainerColors, - pub active: ContainerColors, - pub disabled: ContainerColors, - pub inverted: ContainerColors, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct ContainerColors { - pub background: Hsla, - pub foreground: Hsla, - pub border: Hsla, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct PlayerColors { - pub selection: Hsla, - pub cursor: Hsla, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct Shadow { - pub blur: u8, - pub color: Hsla, - pub offset: Vec, -} - -fn deserialize_player_colors<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - struct PlayerArrayVisitor; - - impl<'de> Visitor<'de> for PlayerArrayVisitor { - type Value = Vec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an object with integer keys") - } - - fn visit_map>( - self, - mut map: A, - ) -> Result { - let mut players = Vec::with_capacity(8); - while let Some((key, value)) = map.next_entry::()? { - if key < 8 { - players.push(value); - } else { - return Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Unsigned(key as u64), - &"a key in range 0..7", - )); - } - } - Ok(players) - } - } - - deserializer.deserialize_map(PlayerArrayVisitor) -} - -fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - #[derive(Deserialize)] - struct ColorWrapper { - color: Hsla, - } - - struct SyntaxVisitor; - - impl<'de> Visitor<'de> for SyntaxVisitor { - type Value = HashMap; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map with keys and objects with a single color field as values") - } - - fn visit_map(self, mut map: M) -> Result, M::Error> - where - M: serde::de::MapAccess<'de>, - { - let mut result = HashMap::new(); - while let Some(key) = map.next_key()? { - let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla - result.insert(key, wrapper.color); - } - Ok(result) - } - } - deserializer.deserialize_map(SyntaxVisitor) -} - -pub fn theme(cx: &AppContext) -> Arc { - theme2::active_theme(cx).clone() -} From 5ff70f7dbab60cda01d0e17e3941c6410d0a890e Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 14:49:31 -0400 Subject: [PATCH 37/54] keeping this bad boy green during fmt checks --- crates/ui2/src/elements/icon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index ef36442296..2273ec24f2 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -36,7 +36,7 @@ impl IconColor { IconColor::Error => gpui2::red(), IconColor::Warning => gpui2::red(), IconColor::Success => gpui2::red(), - IconColor::Info => gpui2::red() + IconColor::Info => gpui2::red(), } } } From bc4f8fbf4e88f1c9d5139b840fbc58c557b94370 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 30 Oct 2023 19:53:48 +0100 Subject: [PATCH 38/54] Rename other references from "handle" to "model" Co-Authored-By: Max Co-Authored-By: Mikayla --- crates/call2/src/call2.rs | 6 +- crates/call2/src/participant.rs | 4 +- crates/call2/src/room.rs | 8 +- crates/client2/src/client2.rs | 16 +-- crates/copilot2/src/copilot2.rs | 8 +- crates/gpui2/src/app.rs | 10 +- crates/gpui2/src/app/entity_map.rs | 140 +++++++++++++------------- crates/gpui2/src/app/model_context.rs | 12 +-- crates/gpui2/src/interactive.rs | 2 +- crates/gpui2/src/view.rs | 4 +- crates/gpui2/src/window.rs | 14 +-- crates/project2/src/project2.rs | 26 ++--- crates/project2/src/terminals.rs | 6 +- 13 files changed, 128 insertions(+), 128 deletions(-) diff --git a/crates/call2/src/call2.rs b/crates/call2/src/call2.rs index ffa2e5e9dc..d8678b7ed4 100644 --- a/crates/call2/src/call2.rs +++ b/crates/call2/src/call2.rs @@ -13,7 +13,7 @@ use collections::HashSet; use futures::{future::Shared, FutureExt}; use gpui2::{ AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task, - WeakHandle, + WeakModel, }; use postage::watch; use project2::Project; @@ -42,7 +42,7 @@ pub struct IncomingCall { pub struct ActiveCall { room: Option<(Model, Vec)>, pending_room_creation: Option, Arc>>>>, - location: Option>, + location: Option>, pending_invites: HashSet, incoming_call: ( watch::Sender>, @@ -347,7 +347,7 @@ impl ActiveCall { } } - pub fn location(&self) -> Option<&WeakHandle> { + pub fn location(&self) -> Option<&WeakModel> { self.location.as_ref() } diff --git a/crates/call2/src/participant.rs b/crates/call2/src/participant.rs index c5c873a78a..7f3e91dbba 100644 --- a/crates/call2/src/participant.rs +++ b/crates/call2/src/participant.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use client2::ParticipantIndex; use client2::{proto, User}; -use gpui2::WeakHandle; +use gpui2::WeakModel; pub use live_kit_client::Frame; use project2::Project; use std::{fmt, sync::Arc}; @@ -33,7 +33,7 @@ impl ParticipantLocation { #[derive(Clone, Default)] pub struct LocalParticipant { pub projects: Vec, - pub active_project: Option>, + pub active_project: Option>, } #[derive(Clone, Debug)] diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index 07873c4cd5..7f51c64d4b 100644 --- a/crates/call2/src/room.rs +++ b/crates/call2/src/room.rs @@ -16,7 +16,7 @@ use collections::{BTreeMap, HashMap, HashSet}; use fs::Fs; use futures::{FutureExt, StreamExt}; use gpui2::{ - AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakHandle, + AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel, }; use language2::LanguageRegistry; use live_kit_client::{LocalTrackPublication, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate}; @@ -61,8 +61,8 @@ pub struct Room { channel_id: Option, // live_kit: Option, status: RoomStatus, - shared_projects: HashSet>, - joined_projects: HashSet>, + shared_projects: HashSet>, + joined_projects: HashSet>, local_participant: LocalParticipant, remote_participants: BTreeMap, pending_participants: Vec>, @@ -424,7 +424,7 @@ impl Room { } async fn maintain_connection( - this: WeakHandle, + this: WeakModel, client: Arc, mut cx: AsyncAppContext, ) -> Result<()> { diff --git a/crates/client2/src/client2.rs b/crates/client2/src/client2.rs index dcea6ded4e..19e8685c28 100644 --- a/crates/client2/src/client2.rs +++ b/crates/client2/src/client2.rs @@ -14,8 +14,8 @@ use futures::{ future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, TryStreamExt, }; use gpui2::{ - serde_json, AnyHandle, AnyWeakHandle, AppContext, AsyncAppContext, Model, SemanticVersion, - Task, WeakHandle, + serde_json, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task, + WeakModel, }; use lazy_static::lazy_static; use parking_lot::RwLock; @@ -227,7 +227,7 @@ struct ClientState { _reconnect_task: Option>, reconnect_interval: Duration, entities_by_type_and_remote_id: HashMap<(TypeId, u64), WeakSubscriber>, - models_by_message_type: HashMap, + models_by_message_type: HashMap, entity_types_by_message_type: HashMap, #[allow(clippy::type_complexity)] message_handlers: HashMap< @@ -236,7 +236,7 @@ struct ClientState { dyn Send + Sync + Fn( - AnyHandle, + AnyModel, Box, &Arc, AsyncAppContext, @@ -246,7 +246,7 @@ struct ClientState { } enum WeakSubscriber { - Entity { handle: AnyWeakHandle }, + Entity { handle: AnyWeakModel }, Pending(Vec>), } @@ -552,7 +552,7 @@ impl Client { #[track_caller] pub fn add_message_handler( self: &Arc, - entity: WeakHandle, + entity: WeakModel, handler: H, ) -> Subscription where @@ -594,7 +594,7 @@ impl Client { pub fn add_request_handler( self: &Arc, - model: WeakHandle, + model: WeakModel, handler: H, ) -> Subscription where @@ -628,7 +628,7 @@ impl Client { where M: EntityMessage, E: 'static + Send, - H: 'static + Send + Sync + Fn(AnyHandle, TypedEnvelope, Arc, AsyncAppContext) -> F, + H: 'static + Send + Sync + Fn(AnyModel, TypedEnvelope, Arc, AsyncAppContext) -> F, F: 'static + Future> + Send, { let model_type_id = TypeId::of::(); diff --git a/crates/copilot2/src/copilot2.rs b/crates/copilot2/src/copilot2.rs index 42b0e3aa41..c3107a2f47 100644 --- a/crates/copilot2/src/copilot2.rs +++ b/crates/copilot2/src/copilot2.rs @@ -8,7 +8,7 @@ use collections::{HashMap, HashSet}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use gpui2::{ AppContext, AsyncAppContext, Context, EntityId, EventEmitter, Model, ModelContext, Task, - WeakHandle, + WeakModel, }; use language2::{ language_settings::{all_language_settings, language_settings}, @@ -278,7 +278,7 @@ pub struct Copilot { http: Arc, node_runtime: Arc, server: CopilotServer, - buffers: HashSet>, + buffers: HashSet>, server_id: LanguageServerId, _subscription: gpui2::Subscription, } @@ -383,7 +383,7 @@ impl Copilot { new_server_id: LanguageServerId, http: Arc, node_runtime: Arc, - this: WeakHandle, + this: WeakModel, mut cx: AsyncAppContext, ) -> impl Future { async move { @@ -706,7 +706,7 @@ impl Copilot { Ok(()) } - fn unregister_buffer(&mut self, buffer: &WeakHandle) { + fn unregister_buffer(&mut self, buffer: &WeakModel) { if let Ok(server) = self.server.as_running() { if let Some(buffer) = server.registered_buffers.remove(&buffer.entity_id()) { server diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index c4ba6a4724..0a09bb2ff8 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -711,7 +711,7 @@ impl Context for AppContext { type Result = T; /// Build an entity that is owned by the application. The given function will be invoked with - /// a `ModelContext` and must return an object representing the entity. A `Handle` will be returned + /// a `ModelContext` and must return an object representing the entity. A `Model` will be returned /// which can be used to access the entity in a context. fn build_model( &mut self, @@ -724,18 +724,18 @@ impl Context for AppContext { }) } - /// Update the entity referenced by the given handle. The function is passed a mutable reference to the + /// Update the entity referenced by the given model. The function is passed a mutable reference to the /// entity along with a `ModelContext` for the entity. fn update_entity( &mut self, - handle: &Model, + model: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> R { self.update(|cx| { - let mut entity = cx.entities.lease(handle); + let mut entity = cx.entities.lease(model); let result = update( &mut entity, - &mut ModelContext::mutable(cx, handle.downgrade()), + &mut ModelContext::mutable(cx, model.downgrade()), ); cx.entities.end_lease(entity); result diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index 68f0a8fa48..bfd457e4ff 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/crates/gpui2/src/app/entity_map.rs @@ -61,21 +61,21 @@ impl EntityMap { where T: 'static + Send, { - let handle = slot.0; - self.entities.insert(handle.entity_id, Box::new(entity)); - handle + let model = slot.0; + self.entities.insert(model.entity_id, Box::new(entity)); + model } /// Move an entity to the stack. - pub fn lease<'a, T>(&mut self, handle: &'a Model) -> Lease<'a, T> { - self.assert_valid_context(handle); + pub fn lease<'a, T>(&mut self, model: &'a Model) -> Lease<'a, T> { + self.assert_valid_context(model); let entity = Some( self.entities - .remove(handle.entity_id) + .remove(model.entity_id) .expect("Circular entity lease. Is the entity already being updated?"), ); Lease { - handle, + model, entity, entity_type: PhantomData, } @@ -84,18 +84,18 @@ impl EntityMap { /// Return an entity after moving it to the stack. pub fn end_lease(&mut self, mut lease: Lease) { self.entities - .insert(lease.handle.entity_id, lease.entity.take().unwrap()); + .insert(lease.model.entity_id, lease.entity.take().unwrap()); } - pub fn read(&self, handle: &Model) -> &T { - self.assert_valid_context(handle); - self.entities[handle.entity_id].downcast_ref().unwrap() + pub fn read(&self, model: &Model) -> &T { + self.assert_valid_context(model); + self.entities[model.entity_id].downcast_ref().unwrap() } - fn assert_valid_context(&self, handle: &AnyHandle) { + fn assert_valid_context(&self, model: &AnyModel) { debug_assert!( - Weak::ptr_eq(&handle.entity_map, &Arc::downgrade(&self.ref_counts)), - "used a handle with the wrong context" + Weak::ptr_eq(&model.entity_map, &Arc::downgrade(&self.ref_counts)), + "used a model with the wrong context" ); } @@ -115,7 +115,7 @@ impl EntityMap { pub struct Lease<'a, T> { entity: Option, - pub handle: &'a Model, + pub model: &'a Model, entity_type: PhantomData, } @@ -145,13 +145,13 @@ impl<'a, T> Drop for Lease<'a, T> { #[derive(Deref, DerefMut)] pub struct Slot(Model); -pub struct AnyHandle { +pub struct AnyModel { pub(crate) entity_id: EntityId, entity_type: TypeId, entity_map: Weak>, } -impl AnyHandle { +impl AnyModel { fn new(id: EntityId, entity_type: TypeId, entity_map: Weak>) -> Self { Self { entity_id: id, @@ -164,8 +164,8 @@ impl AnyHandle { self.entity_id } - pub fn downgrade(&self) -> AnyWeakHandle { - AnyWeakHandle { + pub fn downgrade(&self) -> AnyWeakModel { + AnyWeakModel { entity_id: self.entity_id, entity_type: self.entity_type, entity_ref_counts: self.entity_map.clone(), @@ -175,7 +175,7 @@ impl AnyHandle { pub fn downcast(&self) -> Option> { if TypeId::of::() == self.entity_type { Some(Model { - any_handle: self.clone(), + any_model: self.clone(), entity_type: PhantomData, }) } else { @@ -184,16 +184,16 @@ impl AnyHandle { } } -impl Clone for AnyHandle { +impl Clone for AnyModel { fn clone(&self) -> Self { if let Some(entity_map) = self.entity_map.upgrade() { let entity_map = entity_map.read(); let count = entity_map .counts .get(self.entity_id) - .expect("detected over-release of a handle"); + .expect("detected over-release of a model"); let prev_count = count.fetch_add(1, SeqCst); - assert_ne!(prev_count, 0, "Detected over-release of a handle."); + assert_ne!(prev_count, 0, "Detected over-release of a model."); } Self { @@ -204,16 +204,16 @@ impl Clone for AnyHandle { } } -impl Drop for AnyHandle { +impl Drop for AnyModel { fn drop(&mut self) { if let Some(entity_map) = self.entity_map.upgrade() { let entity_map = entity_map.upgradable_read(); let count = entity_map .counts .get(self.entity_id) - .expect("Detected over-release of a handle."); + .expect("Detected over-release of a model."); let prev_count = count.fetch_sub(1, SeqCst); - assert_ne!(prev_count, 0, "Detected over-release of a handle."); + assert_ne!(prev_count, 0, "Detected over-release of a model."); if prev_count == 1 { // We were the last reference to this entity, so we can remove it. let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map); @@ -223,31 +223,31 @@ impl Drop for AnyHandle { } } -impl From> for AnyHandle { - fn from(handle: Model) -> Self { - handle.any_handle +impl From> for AnyModel { + fn from(model: Model) -> Self { + model.any_model } } -impl Hash for AnyHandle { +impl Hash for AnyModel { fn hash(&self, state: &mut H) { self.entity_id.hash(state); } } -impl PartialEq for AnyHandle { +impl PartialEq for AnyModel { fn eq(&self, other: &Self) -> bool { self.entity_id == other.entity_id } } -impl Eq for AnyHandle {} +impl Eq for AnyModel {} #[derive(Deref, DerefMut)] pub struct Model { #[deref] #[deref_mut] - any_handle: AnyHandle, + any_model: AnyModel, entity_type: PhantomData, } @@ -260,14 +260,14 @@ impl Model { T: 'static, { Self { - any_handle: AnyHandle::new(id, TypeId::of::(), entity_map), + any_model: AnyModel::new(id, TypeId::of::(), entity_map), entity_type: PhantomData, } } - pub fn downgrade(&self) -> WeakHandle { - WeakHandle { - any_handle: self.any_handle.downgrade(), + pub fn downgrade(&self) -> WeakModel { + WeakModel { + any_model: self.any_model.downgrade(), entity_type: self.entity_type, } } @@ -276,7 +276,7 @@ impl Model { cx.entities.read(self) } - /// Update the entity referenced by this handle with the given function. + /// Update the entity referenced by this model with the given function. /// /// The update function receives a context appropriate for its environment. /// When updating in an `AppContext`, it receives a `ModelContext`. @@ -296,7 +296,7 @@ impl Model { impl Clone for Model { fn clone(&self) -> Self { Self { - any_handle: self.any_handle.clone(), + any_model: self.any_model.clone(), entity_type: self.entity_type, } } @@ -306,8 +306,8 @@ impl std::fmt::Debug for Model { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "Handle {{ entity_id: {:?}, entity_type: {:?} }}", - self.any_handle.entity_id, + "Model {{ entity_id: {:?}, entity_type: {:?} }}", + self.any_model.entity_id, type_name::() ) } @@ -315,32 +315,32 @@ impl std::fmt::Debug for Model { impl Hash for Model { fn hash(&self, state: &mut H) { - self.any_handle.hash(state); + self.any_model.hash(state); } } impl PartialEq for Model { fn eq(&self, other: &Self) -> bool { - self.any_handle == other.any_handle + self.any_model == other.any_model } } impl Eq for Model {} -impl PartialEq> for Model { - fn eq(&self, other: &WeakHandle) -> bool { +impl PartialEq> for Model { + fn eq(&self, other: &WeakModel) -> bool { self.entity_id() == other.entity_id() } } #[derive(Clone)] -pub struct AnyWeakHandle { +pub struct AnyWeakModel { pub(crate) entity_id: EntityId, entity_type: TypeId, entity_ref_counts: Weak>, } -impl AnyWeakHandle { +impl AnyWeakModel { pub fn entity_id(&self) -> EntityId { self.entity_id } @@ -354,14 +354,14 @@ impl AnyWeakHandle { ref_count > 0 } - pub fn upgrade(&self) -> Option { + pub fn upgrade(&self) -> Option { let entity_map = self.entity_ref_counts.upgrade()?; entity_map .read() .counts .get(self.entity_id)? .fetch_add(1, SeqCst); - Some(AnyHandle { + Some(AnyModel { entity_id: self.entity_id, entity_type: self.entity_type, entity_map: self.entity_ref_counts.clone(), @@ -369,55 +369,55 @@ impl AnyWeakHandle { } } -impl From> for AnyWeakHandle { - fn from(handle: WeakHandle) -> Self { - handle.any_handle +impl From> for AnyWeakModel { + fn from(model: WeakModel) -> Self { + model.any_model } } -impl Hash for AnyWeakHandle { +impl Hash for AnyWeakModel { fn hash(&self, state: &mut H) { self.entity_id.hash(state); } } -impl PartialEq for AnyWeakHandle { +impl PartialEq for AnyWeakModel { fn eq(&self, other: &Self) -> bool { self.entity_id == other.entity_id } } -impl Eq for AnyWeakHandle {} +impl Eq for AnyWeakModel {} #[derive(Deref, DerefMut)] -pub struct WeakHandle { +pub struct WeakModel { #[deref] #[deref_mut] - any_handle: AnyWeakHandle, + any_model: AnyWeakModel, entity_type: PhantomData, } -unsafe impl Send for WeakHandle {} -unsafe impl Sync for WeakHandle {} +unsafe impl Send for WeakModel {} +unsafe impl Sync for WeakModel {} -impl Clone for WeakHandle { +impl Clone for WeakModel { fn clone(&self) -> Self { Self { - any_handle: self.any_handle.clone(), + any_model: self.any_model.clone(), entity_type: self.entity_type, } } } -impl WeakHandle { +impl WeakModel { pub fn upgrade(&self) -> Option> { Some(Model { - any_handle: self.any_handle.upgrade()?, + any_model: self.any_model.upgrade()?, entity_type: self.entity_type, }) } - /// Update the entity referenced by this handle with the given function if + /// Update the entity referenced by this model with the given function if /// the referenced entity still exists. Returns an error if the entity has /// been released. /// @@ -441,21 +441,21 @@ impl WeakHandle { } } -impl Hash for WeakHandle { +impl Hash for WeakModel { fn hash(&self, state: &mut H) { - self.any_handle.hash(state); + self.any_model.hash(state); } } -impl PartialEq for WeakHandle { +impl PartialEq for WeakModel { fn eq(&self, other: &Self) -> bool { - self.any_handle == other.any_handle + self.any_model == other.any_model } } -impl Eq for WeakHandle {} +impl Eq for WeakModel {} -impl PartialEq> for WeakHandle { +impl PartialEq> for WeakModel { fn eq(&self, other: &Model) -> bool { self.entity_id() == other.entity_id() } diff --git a/crates/gpui2/src/app/model_context.rs b/crates/gpui2/src/app/model_context.rs index b5f78fbc46..463652886b 100644 --- a/crates/gpui2/src/app/model_context.rs +++ b/crates/gpui2/src/app/model_context.rs @@ -1,6 +1,6 @@ use crate::{ AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, MainThread, Model, - Reference, Subscription, Task, WeakHandle, + Reference, Subscription, Task, WeakModel, }; use derive_more::{Deref, DerefMut}; use futures::FutureExt; @@ -15,11 +15,11 @@ pub struct ModelContext<'a, T> { #[deref] #[deref_mut] app: Reference<'a, AppContext>, - model_state: WeakHandle, + model_state: WeakModel, } impl<'a, T: 'static> ModelContext<'a, T> { - pub(crate) fn mutable(app: &'a mut AppContext, model_state: WeakHandle) -> Self { + pub(crate) fn mutable(app: &'a mut AppContext, model_state: WeakModel) -> Self { Self { app: Reference::Mutable(app), model_state, @@ -36,7 +36,7 @@ impl<'a, T: 'static> ModelContext<'a, T> { .expect("The entity must be alive if we have a model context") } - pub fn weak_handle(&self) -> WeakHandle { + pub fn weak_handle(&self) -> WeakModel { self.model_state.clone() } @@ -184,7 +184,7 @@ impl<'a, T: 'static> ModelContext<'a, T> { pub fn spawn( &self, - f: impl FnOnce(WeakHandle, AsyncAppContext) -> Fut + Send + 'static, + f: impl FnOnce(WeakModel, AsyncAppContext) -> Fut + Send + 'static, ) -> Task where T: 'static, @@ -197,7 +197,7 @@ impl<'a, T: 'static> ModelContext<'a, T> { pub fn spawn_on_main( &self, - f: impl FnOnce(WeakHandle, MainThread) -> Fut + Send + 'static, + f: impl FnOnce(WeakModel, MainThread) -> Fut + Send + 'static, ) -> Task where Fut: Future + 'static, diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index a617792bfb..faa7d23975 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -333,7 +333,7 @@ pub trait StatefulInteractive: StatelessInteractive { Some(Box::new(move |view_state, cursor_offset, cx| { let drag = listener(view_state, cx); let drag_handle_view = Some( - View::for_handle(cx.handle().upgrade().unwrap(), move |view_state, cx| { + View::for_handle(cx.model().upgrade().unwrap(), move |view_state, cx| { (drag.render_drag_handle)(view_state, cx) }) .into_any(), diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index c988223fd0..cacca8b91e 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -1,6 +1,6 @@ use crate::{ AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId, - EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, WeakHandle, WindowContext, + EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, WeakModel, WindowContext, }; use anyhow::{Context, Result}; use parking_lot::Mutex; @@ -116,7 +116,7 @@ impl Element<()> for View { } pub struct WeakView { - pub(crate) state: WeakHandle, + pub(crate) state: WeakModel, render: Weak) -> AnyElement + Send + 'static>>, } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 073ffa56bd..3d6a891dfe 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -7,7 +7,7 @@ use crate::{ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, - TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakHandle, WeakView, + TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakModel, WeakView, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; @@ -1257,13 +1257,13 @@ impl Context for WindowContext<'_, '_> { fn update_entity( &mut self, - handle: &Model, + model: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> R { - let mut entity = self.entities.lease(handle); + let mut entity = self.entities.lease(model); let result = update( &mut *entity, - &mut ModelContext::mutable(&mut *self.app, handle.downgrade()), + &mut ModelContext::mutable(&mut *self.app, model.downgrade()), ); self.entities.end_lease(entity); result @@ -1555,7 +1555,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { self.view.clone() } - pub fn handle(&self) -> WeakHandle { + pub fn model(&self) -> WeakModel { self.view.state.clone() } @@ -1872,10 +1872,10 @@ impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> { fn update_entity( &mut self, - handle: &Model, + model: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> R { - self.window_cx.update_entity(handle, update) + self.window_cx.update_entity(model, update) } } diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index e91c3b7263..ce97b9cc22 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -26,8 +26,8 @@ use futures::{ }; use globset::{Glob, GlobSet, GlobSetBuilder}; use gpui2::{ - AnyHandle, AppContext, AsyncAppContext, Context, EventEmitter, Executor, Model, ModelContext, - Task, WeakHandle, + AnyModel, AppContext, AsyncAppContext, Context, EventEmitter, Executor, Model, ModelContext, + Task, WeakModel, }; use itertools::Itertools; use language2::{ @@ -153,7 +153,7 @@ pub struct Project { incomplete_remote_buffers: HashMap>>, buffer_snapshots: HashMap>>, // buffer_id -> server_id -> vec of snapshots buffers_being_formatted: HashSet, - buffers_needing_diff: HashSet>, + buffers_needing_diff: HashSet>, git_diff_debouncer: DelayedDebounced, nonce: u128, _maintain_buffer_languages: Task<()>, @@ -245,14 +245,14 @@ enum LocalProjectUpdate { enum OpenBuffer { Strong(Model), - Weak(WeakHandle), + Weak(WeakModel), Operations(Vec), } #[derive(Clone)] enum WorktreeHandle { Strong(Model), - Weak(WeakHandle), + Weak(WeakModel), } enum ProjectClientState { @@ -1671,7 +1671,7 @@ impl Project { &mut self, path: impl Into, cx: &mut ModelContext, - ) -> Task> { + ) -> Task> { let task = self.open_buffer(path, cx); cx.spawn(move |_, mut cx| async move { let buffer = task.await?; @@ -1681,7 +1681,7 @@ impl Project { })? .ok_or_else(|| anyhow!("no project entry"))?; - let buffer: &AnyHandle = &buffer; + let buffer: &AnyModel = &buffer; Ok((project_entry_id, buffer.clone())) }) } @@ -2158,7 +2158,7 @@ impl Project { } async fn send_buffer_ordered_messages( - this: WeakHandle, + this: WeakModel, rx: UnboundedReceiver, mut cx: AsyncAppContext, ) -> Result<()> { @@ -2166,7 +2166,7 @@ impl Project { let mut operations_by_buffer_id = HashMap::default(); async fn flush_operations( - this: &WeakHandle, + this: &WeakModel, operations_by_buffer_id: &mut HashMap>, needs_resync_with_host: &mut bool, is_local: bool, @@ -2931,7 +2931,7 @@ impl Project { } async fn setup_and_insert_language_server( - this: WeakHandle, + this: WeakModel, initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, @@ -2970,7 +2970,7 @@ impl Project { } async fn setup_pending_language_server( - this: WeakHandle, + this: WeakModel, initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, @@ -3748,7 +3748,7 @@ impl Project { } async fn on_lsp_workspace_edit( - this: WeakHandle, + this: WeakModel, params: lsp2::ApplyWorkspaceEditParams, server_id: LanguageServerId, adapter: Arc, @@ -4360,7 +4360,7 @@ impl Project { } async fn format_via_lsp( - this: &WeakHandle, + this: &WeakModel, buffer: &Model, abs_path: &Path, language_server: &Arc, diff --git a/crates/project2/src/terminals.rs b/crates/project2/src/terminals.rs index 239cb99d86..5cd62d5ae6 100644 --- a/crates/project2/src/terminals.rs +++ b/crates/project2/src/terminals.rs @@ -1,5 +1,5 @@ use crate::Project; -use gpui2::{AnyWindowHandle, Context, Model, ModelContext, WeakHandle}; +use gpui2::{AnyWindowHandle, Context, Model, ModelContext, WeakModel}; use settings2::Settings; use std::path::{Path, PathBuf}; use terminal2::{ @@ -11,7 +11,7 @@ use terminal2::{ use std::os::unix::ffi::OsStrExt; pub struct Terminals { - pub(crate) local_handles: Vec>, + pub(crate) local_handles: Vec>, } impl Project { @@ -121,7 +121,7 @@ impl Project { } } - pub fn local_terminal_handles(&self) -> &Vec> { + pub fn local_terminal_handles(&self) -> &Vec> { &self.terminals.local_handles } } From c17b246bac4e57be486f008f712bd63a191c91cb Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 15:04:16 -0400 Subject: [PATCH 39/54] updated for model handle rename --- crates/ai2/src/prompts/repository_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ai2/src/prompts/repository_context.rs b/crates/ai2/src/prompts/repository_context.rs index 78db5a1651..1bb75de7d2 100644 --- a/crates/ai2/src/prompts/repository_context.rs +++ b/crates/ai2/src/prompts/repository_context.rs @@ -2,7 +2,7 @@ use crate::prompts::base::{PromptArguments, PromptTemplate}; use std::fmt::Write; use std::{ops::Range, path::PathBuf}; -use gpui2::{AsyncAppContext, Handle}; +use gpui2::{AsyncAppContext, Model}; use language2::{Anchor, Buffer}; #[derive(Clone)] @@ -14,7 +14,7 @@ pub struct PromptCodeSnippet { impl PromptCodeSnippet { pub fn new( - buffer: Handle, + buffer: Model, range: Range, cx: &mut AsyncAppContext, ) -> anyhow::Result { From 942167e046ba9478ff284408d5752410b091e76a Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 15:17:11 -0400 Subject: [PATCH 40/54] Format `ui2` --- crates/ui2/src/elements/icon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index ef36442296..2273ec24f2 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -36,7 +36,7 @@ impl IconColor { IconColor::Error => gpui2::red(), IconColor::Warning => gpui2::red(), IconColor::Success => gpui2::red(), - IconColor::Info => gpui2::red() + IconColor::Info => gpui2::red(), } } } From 0128079de0783c775940864e182b91fd5a4882a6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 30 Oct 2023 20:36:48 +0100 Subject: [PATCH 41/54] WIP --- crates/gpui2/src/app.rs | 5 +- crates/gpui2/src/element.rs | 2 +- crates/gpui2/src/interactive.rs | 15 +++++- crates/gpui2/src/view.rs | 87 ++++++++++++++------------------- crates/gpui2/src/window.rs | 13 +++-- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 0a09bb2ff8..0523609db3 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -15,7 +15,7 @@ pub use test_context::*; use crate::{ current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AppMetadata, AssetSource, ClipboardItem, Context, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, - KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point, + KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId, }; @@ -815,7 +815,7 @@ impl MainThread { /// Opens a new window with the given option and the root view returned by the given function. /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific /// functionality. - pub fn open_window( + pub fn open_window( &mut self, options: crate::WindowOptions, build_root_view: impl FnOnce(&mut WindowContext) -> View + Send + 'static, @@ -898,6 +898,7 @@ impl DerefMut for GlobalLease { /// Contains state associated with an active drag operation, started by dragging an element /// within the window or by dragging into the app from the underlying platform. pub(crate) struct AnyDrag { + pub render: Box AnyElement<()>>, pub drag_handle_view: Option, pub cursor_offset: Point, pub state: AnyBox, diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 6dc5bc0a93..a715ed30ee 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -4,7 +4,7 @@ pub(crate) use smallvec::SmallVec; use std::{any::Any, mem}; pub trait Element { - type ElementState: 'static; + type ElementState: 'static + Send; fn id(&self) -> Option; diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index faa7d23975..43fd83a55f 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -333,12 +333,19 @@ pub trait StatefulInteractive: StatelessInteractive { Some(Box::new(move |view_state, cursor_offset, cx| { let drag = listener(view_state, cx); let drag_handle_view = Some( - View::for_handle(cx.model().upgrade().unwrap(), move |view_state, cx| { - (drag.render_drag_handle)(view_state, cx) + cx.build_view(|cx| DragView { + model: cx.model().upgrade().unwrap(), + drag, }) .into_any(), ); AnyDrag { + render: { + let view = cx.view(); + Box::new(move |cx| { + view.update(cx, |view, cx| drag.render_drag_handle(view, cx)) + }) + }, drag_handle_view, cursor_offset, state: Box::new(drag.state), @@ -888,6 +895,10 @@ where } } +// impl Render for Drag { +// // fn render(&mut self, cx: ViewContext) -> +// } + #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] pub enum MouseButton { Left, diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index cacca8b91e..630f2f9864 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -3,48 +3,31 @@ use crate::{ EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, WeakModel, WindowContext, }; use anyhow::{Context, Result}; -use parking_lot::Mutex; -use std::{ - marker::PhantomData, - sync::{Arc, Weak}, -}; +use std::{marker::PhantomData, sync::Arc}; + +pub trait Render: 'static + Sized { + type Element: Element + 'static + Send; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element; +} pub struct View { - pub(crate) state: Model, - render: Arc) -> AnyElement + Send + 'static>>, + pub(crate) model: Model, } -impl View { - pub fn for_handle( - state: Model, - render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, - ) -> View - where - E: Component, - { - View { - state, - render: Arc::new(Mutex::new( - move |state: &mut V, cx: &mut ViewContext<'_, '_, V>| render(state, cx).render(), - )), - } - } -} - -impl View { +impl View { pub fn into_any(self) -> AnyView { AnyView(Arc::new(self)) } - - pub fn downgrade(&self) -> WeakView { - WeakView { - state: self.state.downgrade(), - render: Arc::downgrade(&self.render), - } - } } impl View { + pub fn downgrade(&self) -> WeakView { + WeakView { + model: self.model.downgrade(), + } + } + pub fn update( &self, cx: &mut C, @@ -60,13 +43,12 @@ impl View { impl Clone for View { fn clone(&self) -> Self { Self { - state: self.state.clone(), - render: self.render.clone(), + model: self.model.clone(), } } } -impl Component for View { +impl Component for View { fn render(self) -> AnyElement { AnyElement::new(EraseViewState { view: self, @@ -75,11 +57,14 @@ impl Component for View Element<()> for View { +impl Element<()> for View +where + V: Render, +{ type ElementState = AnyElement; fn id(&self) -> Option { - Some(ElementId::View(self.state.entity_id)) + Some(ElementId::View(self.model.entity_id)) } fn initialize( @@ -89,7 +74,7 @@ impl Element<()> for View { cx: &mut ViewContext<()>, ) -> Self::ElementState { self.update(cx, |state, cx| { - let mut any_element = (self.render.lock())(state, cx); + let mut any_element = AnyElement::new(state.render(cx)); any_element.initialize(state, cx); any_element }) @@ -116,15 +101,13 @@ impl Element<()> for View { } pub struct WeakView { - pub(crate) state: WeakModel, - render: Weak) -> AnyElement + Send + 'static>>, + pub(crate) model: WeakModel, } impl WeakView { pub fn upgrade(&self) -> Option> { - let state = self.state.upgrade()?; - let render = self.render.upgrade()?; - Some(View { state, render }) + let model = self.model.upgrade()?; + Some(View { model }) } pub fn update( @@ -140,8 +123,7 @@ impl WeakView { impl Clone for WeakView { fn clone(&self) -> Self { Self { - state: self.state.clone(), - render: self.render.clone(), + model: self.model.clone(), } } } @@ -153,13 +135,13 @@ struct EraseViewState { unsafe impl Send for EraseViewState {} -impl Component for EraseViewState { +impl Component for EraseViewState { fn render(self) -> AnyElement { AnyElement::new(self) } } -impl Element for EraseViewState { +impl Element for EraseViewState { type ElementState = AnyBox; fn id(&self) -> Option { @@ -202,17 +184,20 @@ trait ViewObject: Send + Sync { fn paint(&self, bounds: Bounds, element: &mut AnyBox, cx: &mut WindowContext); } -impl ViewObject for View { +impl ViewObject for View +where + V: Render, +{ fn entity_id(&self) -> EntityId { - self.state.entity_id + self.model.entity_id } fn initialize(&self, cx: &mut WindowContext) -> AnyBox { cx.with_element_id(self.entity_id(), |_global_id, cx| { self.update(cx, |state, cx| { - let mut any_element = Box::new((self.render.lock())(state, cx)); + let mut any_element = Box::new(AnyElement::new(state.render(cx))); any_element.initialize(state, cx); - any_element as AnyBox + any_element }) }) } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 3d6a891dfe..86732cd21e 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -900,6 +900,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { if let Some(drag_handle_view) = &mut active_drag.drag_handle_view { drag_handle_view.draw(available_space, cx); } + if let Some(render) = &mut active_drag.render { + (render)() + } cx.active_drag = Some(active_drag); }); }); @@ -1300,7 +1303,7 @@ impl VisualContext for WindowContext<'_, '_> { view: &View, update: impl FnOnce(&mut T, &mut Self::ViewContext<'_, '_, T>) -> R, ) -> Self::Result { - let mut lease = self.app.entities.lease(&view.state); + let mut lease = self.app.entities.lease(&view.model); let mut cx = ViewContext::mutable(&mut *self.app, &mut *self.window, view.downgrade()); let result = update(&mut *lease, &mut cx); cx.app.entities.end_lease(lease); @@ -1556,7 +1559,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { } pub fn model(&self) -> WeakModel { - self.view.state.clone() + self.view.model.clone() } pub fn stack(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R { @@ -1635,7 +1638,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { ) -> Subscription { let window_handle = self.window.handle; self.app.release_listeners.insert( - self.view.state.entity_id, + self.view.model.entity_id, Box::new(move |this, cx| { let this = this.downcast_mut().expect("invalid entity type"); // todo!("are we okay with silently swallowing the error?") @@ -1668,7 +1671,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { pub fn notify(&mut self) { self.window_cx.notify(); self.window_cx.app.push_effect(Effect::Notify { - emitter: self.view.state.entity_id, + emitter: self.view.model.entity_id, }); } @@ -1848,7 +1851,7 @@ where V::Event: Any + Send, { pub fn emit(&mut self, event: V::Event) { - let emitter = self.view.state.entity_id; + let emitter = self.view.model.entity_id; self.app.push_effect(Effect::Emit { emitter, event: Box::new(event), From b34f0c3bee3b65fa8f41f2977f541588c742be0e Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 16:21:58 -0400 Subject: [PATCH 42/54] update prettier2, call2 and project2 to use fs2 --- Cargo.lock | 6 +++--- crates/call2/Cargo.toml | 4 ++-- crates/call2/src/room.rs | 2 +- crates/prettier2/Cargo.toml | 4 ++-- crates/prettier2/src/prettier2.rs | 2 +- crates/project2/Cargo.toml | 4 ++-- crates/project2/src/project2.rs | 8 ++++---- crates/project2/src/worktree.rs | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5d187d08e..85eb0fde85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1165,7 +1165,7 @@ dependencies = [ "audio2", "client2", "collections", - "fs", + "fs2", "futures 0.3.28", "gpui2", "language2", @@ -6034,7 +6034,7 @@ dependencies = [ "anyhow", "client2", "collections", - "fs", + "fs2", "futures 0.3.28", "gpui2", "language2", @@ -6201,7 +6201,7 @@ dependencies = [ "ctor", "db2", "env_logger 0.9.3", - "fs", + "fs2", "fsevent", "futures 0.3.28", "fuzzy2", diff --git a/crates/call2/Cargo.toml b/crates/call2/Cargo.toml index efc7ab326e..f0e47832ed 100644 --- a/crates/call2/Cargo.toml +++ b/crates/call2/Cargo.toml @@ -25,7 +25,7 @@ collections = { path = "../collections" } gpui2 = { path = "../gpui2" } log.workspace = true live_kit_client = { path = "../live_kit_client" } -fs = { path = "../fs" } +fs2 = { path = "../fs2" } language2 = { path = "../language2" } media = { path = "../media" } project2 = { path = "../project2" } @@ -43,7 +43,7 @@ serde_derive.workspace = true [dev-dependencies] client2 = { path = "../client2", features = ["test-support"] } -fs = { path = "../fs", features = ["test-support"] } +fs2 = { path = "../fs2", features = ["test-support"] } language2 = { path = "../language2", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } gpui2 = { path = "../gpui2", features = ["test-support"] } diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index 7f51c64d4b..b7bac52a8b 100644 --- a/crates/call2/src/room.rs +++ b/crates/call2/src/room.rs @@ -13,7 +13,7 @@ use client2::{ Client, ParticipantIndex, TypedEnvelope, User, UserStore, }; use collections::{BTreeMap, HashMap, HashSet}; -use fs::Fs; +use fs2::Fs; use futures::{FutureExt, StreamExt}; use gpui2::{ AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel, diff --git a/crates/prettier2/Cargo.toml b/crates/prettier2/Cargo.toml index 8defd40262..b98124f72c 100644 --- a/crates/prettier2/Cargo.toml +++ b/crates/prettier2/Cargo.toml @@ -16,7 +16,7 @@ client2 = { path = "../client2" } collections = { path = "../collections"} language2 = { path = "../language2" } gpui2 = { path = "../gpui2" } -fs = { path = "../fs" } +fs2 = { path = "../fs2" } lsp2 = { path = "../lsp2" } node_runtime = { path = "../node_runtime"} util = { path = "../util" } @@ -32,4 +32,4 @@ parking_lot.workspace = true [dev-dependencies] language2 = { path = "../language2", features = ["test-support"] } gpui2 = { path = "../gpui2", features = ["test-support"] } -fs = { path = "../fs", features = ["test-support"] } +fs2 = { path = "../fs2", features = ["test-support"] } diff --git a/crates/prettier2/src/prettier2.rs b/crates/prettier2/src/prettier2.rs index 52e5971a80..804aeab594 100644 --- a/crates/prettier2/src/prettier2.rs +++ b/crates/prettier2/src/prettier2.rs @@ -1,6 +1,6 @@ use anyhow::Context; use collections::{HashMap, HashSet}; -use fs::Fs; +use fs2::Fs; use gpui2::{AsyncAppContext, Model}; use language2::{language_settings::language_settings, Buffer, BundledFormatter, Diff}; use lsp2::{LanguageServer, LanguageServerId}; diff --git a/crates/project2/Cargo.toml b/crates/project2/Cargo.toml index 98bf9b62be..b135b5367c 100644 --- a/crates/project2/Cargo.toml +++ b/crates/project2/Cargo.toml @@ -25,7 +25,7 @@ client2 = { path = "../client2" } clock = { path = "../clock" } collections = { path = "../collections" } db2 = { path = "../db2" } -fs = { path = "../fs" } +fs2 = { path = "../fs2" } fsevent = { path = "../fsevent" } fuzzy2 = { path = "../fuzzy2" } git = { path = "../git" } @@ -71,7 +71,7 @@ pretty_assertions.workspace = true client2 = { path = "../client2", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } db2 = { path = "../db2", features = ["test-support"] } -fs = { path = "../fs", features = ["test-support"] } +fs2 = { path = "../fs2", features = ["test-support"] } gpui2 = { path = "../gpui2", features = ["test-support"] } language2 = { path = "../language2", features = ["test-support"] } lsp2 = { path = "../lsp2", features = ["test-support"] } diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index ce97b9cc22..c2ee171866 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -89,7 +89,7 @@ use util::{ post_inc, ResultExt, TryFutureExt as _, }; -pub use fs::*; +pub use fs2::*; pub use worktree::*; const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4; @@ -5201,7 +5201,7 @@ impl Project { fs.create_file( &abs_path, op.options - .map(|options| fs::CreateOptions { + .map(|options| fs2::CreateOptions { overwrite: options.overwrite.unwrap_or(false), ignore_if_exists: options.ignore_if_exists.unwrap_or(false), }) @@ -5224,7 +5224,7 @@ impl Project { &source_abs_path, &target_abs_path, op.options - .map(|options| fs::RenameOptions { + .map(|options| fs2::RenameOptions { overwrite: options.overwrite.unwrap_or(false), ignore_if_exists: options.ignore_if_exists.unwrap_or(false), }) @@ -5240,7 +5240,7 @@ impl Project { .map_err(|_| anyhow!("can't convert URI to path"))?; let options = op .options - .map(|options| fs::RemoveOptions { + .map(|options| fs2::RemoveOptions { recursive: options.recursive.unwrap_or(false), ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), }) diff --git a/crates/project2/src/worktree.rs b/crates/project2/src/worktree.rs index 00ab10d8e8..d73769cc00 100644 --- a/crates/project2/src/worktree.rs +++ b/crates/project2/src/worktree.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Context as _, Result}; use client2::{proto, Client}; use clock::ReplicaId; use collections::{HashMap, HashSet, VecDeque}; -use fs::{ +use fs2::{ repository::{GitFileStatus, GitRepository, RepoPath}, Fs, }; @@ -2815,7 +2815,7 @@ pub type UpdatedGitRepositoriesSet = Arc<[(Arc, GitRepositoryChange)]>; impl Entry { fn new( path: Arc, - metadata: &fs::Metadata, + metadata: &fs2::Metadata, next_entry_id: &AtomicUsize, root_char_bag: CharBag, ) -> Self { From 7841a56a11bde8ef72bdcacbfae1b80ade1b3a20 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 16:21:59 -0400 Subject: [PATCH 43/54] Start work on defining color scales --- crates/theme2/src/default.rs | 101 +++++++++++++++++++++ crates/theme2/src/scale.rs | 166 +++++++++++++++++++++++++++++++++++ crates/theme2/src/theme2.rs | 8 ++ 3 files changed, 275 insertions(+) create mode 100644 crates/theme2/src/default.rs create mode 100644 crates/theme2/src/scale.rs diff --git a/crates/theme2/src/default.rs b/crates/theme2/src/default.rs new file mode 100644 index 0000000000..d1550f9e78 --- /dev/null +++ b/crates/theme2/src/default.rs @@ -0,0 +1,101 @@ +use std::collections::HashMap; + +use gpui2::Rgba; + +use crate::scale::{ColorScaleName, ColorScaleSet, ColorScales}; + +struct DefaultColorScaleSet { + scale: ColorScaleName, + light: [&'static str; 12], + light_alpha: [&'static str; 12], + dark: [&'static str; 12], + dark_alpha: [&'static str; 12], +} + +impl From for ColorScaleSet { + fn from(default: DefaultColorScaleSet) -> Self { + Self::new( + default.scale, + default + .light + .map(|color| Rgba::try_from(color).unwrap().into()), + default + .light_alpha + .map(|color| Rgba::try_from(color).unwrap().into()), + default + .dark + .map(|color| Rgba::try_from(color).unwrap().into()), + default + .dark_alpha + .map(|color| Rgba::try_from(color).unwrap().into()), + ) + } +} + +pub fn default_color_scales() -> ColorScales { + use ColorScaleName::*; + + HashMap::from_iter([(Red, red().into())]) +} + +fn red() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Red, + light: [ + "#fffcfc00", + "#fff7f700", + "#feebec00", + "#ffdbdc00", + "#ffcdce00", + "#fdbdbe00", + "#f4a9aa00", + "#eb8e9000", + "#e5484d00", + "#dc3e4200", + "#ce2c3100", + "#64172300", + ], + light_alpha: [ + "#ff000003", + "#ff000008", + "#f3000d14", + "#ff000824", + "#ff000632", + "#f8000442", + "#df000356", + "#d2000571", + "#db0007b7", + "#d10005c1", + "#c40006d3", + "#55000de8", + ], + dark: [ + "#19111100", + "#20131400", + "#3b121900", + "#500f1c00", + "#61162300", + "#72232d00", + "#8c333a00", + "#b5454800", + "#e5484d00", + "#ec5d5e00", + "#ff959200", + "#ffd1d900", + ], + dark_alpha: [ + "#f4121209", + "#f22f3e11", + "#ff173f2d", + "#fe0a3b44", + "#ff204756", + "#ff3e5668", + "#ff536184", + "#ff5d61b0", + "#fe4e54e4", + "#ff6465eb", + "#ff959200", + "#ffd1d900", + ], + } +} diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs new file mode 100644 index 0000000000..a7bd6a5e22 --- /dev/null +++ b/crates/theme2/src/scale.rs @@ -0,0 +1,166 @@ +use std::collections::HashMap; + +use gpui2::{AppContext, Hsla}; + +use crate::{theme, Appearance}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ColorScaleName { + Gray, + Mauve, + Slate, + Sage, + Olive, + Sand, + Gold, + Bronze, + Brown, + Yellow, + Amber, + Orange, + Tomato, + Red, + Ruby, + Crimson, + Pink, + Plum, + Purple, + Violet, + Iris, + Indigo, + Blue, + Cyan, + Teal, + Jade, + Green, + Grass, + Lime, + Mint, + Sky, + Black, + White, +} + +impl std::fmt::Display for ColorScaleName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Gray => "Gray", + Self::Mauve => "Mauve", + Self::Slate => "Slate", + Self::Sage => "Sage", + Self::Olive => "Olive", + Self::Sand => "Sand", + Self::Gold => "Gold", + Self::Bronze => "Bronze", + Self::Brown => "Brown", + Self::Yellow => "Yellow", + Self::Amber => "Amber", + Self::Orange => "Orange", + Self::Tomato => "Tomato", + Self::Red => "Red", + Self::Ruby => "Ruby", + Self::Crimson => "Crimson", + Self::Pink => "Pink", + Self::Plum => "Plum", + Self::Purple => "Purple", + Self::Violet => "Violet", + Self::Iris => "Iris", + Self::Indigo => "Indigo", + Self::Blue => "Blue", + Self::Cyan => "Cyan", + Self::Teal => "Teal", + Self::Jade => "Jade", + Self::Green => "Green", + Self::Grass => "Grass", + Self::Lime => "Lime", + Self::Mint => "Mint", + Self::Sky => "Sky", + Self::Black => "Black", + Self::White => "White", + } + ) + } +} + +pub type ColorScale = [Hsla; 12]; + +pub type ColorScales = HashMap; + +pub struct ColorScaleSet { + name: ColorScaleName, + light: ColorScale, + dark: ColorScale, + light_alpha: ColorScale, + dark_alpha: ColorScale, +} + +impl ColorScaleSet { + pub fn new( + name: ColorScaleName, + light: ColorScale, + light_alpha: ColorScale, + dark: ColorScale, + dark_alpha: ColorScale, + ) -> Self { + Self { + name, + light, + light_alpha, + dark, + dark_alpha, + } + } + + pub fn name(&self) -> String { + self.name.to_string() + } + + pub fn light(&self, step: usize) -> Hsla { + self.light[step - 1] + } + + pub fn light_alpha(&self, step: usize) -> Hsla { + self.light_alpha[step - 1] + } + + pub fn dark(&self, step: usize) -> Hsla { + self.dark[step - 1] + } + + pub fn dark_alpha(&self, step: usize) -> Hsla { + self.dark[step - 1] + } + + fn current_appearance(cx: &AppContext) -> Appearance { + let theme = theme(cx); + if theme.metadata.is_light { + Appearance::Light + } else { + Appearance::Dark + } + } + + /// Returns the one-based step in the scale. + /// + /// We usually reference steps as 1-12 instead of 0-11, so we + /// automatically subtract 1 from the index. + pub fn step(self, cx: &AppContext, index: usize) -> Hsla { + let appearance = Self::current_appearance(cx); + + match appearance { + Appearance::Light => self.light(index), + Appearance::Dark => self.dark(index), + } + } + + pub fn step_alpha(self, cx: &AppContext, index: usize) -> Hsla { + let appearance = Self::current_appearance(cx); + match appearance { + Appearance::Light => self.light_alpha(index), + Appearance::Dark => self.dark_alpha(index), + } + } +} diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 6d4d5269e3..66d70296d2 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -1,4 +1,6 @@ +mod default; mod registry; +mod scale; mod settings; mod themes; @@ -9,6 +11,12 @@ use gpui2::{AppContext, HighlightStyle, Hsla, SharedString}; use settings2::Settings; use std::sync::Arc; +#[derive(Debug, Clone, PartialEq)] +pub enum Appearance { + Light, + Dark, +} + pub fn init(cx: &mut AppContext) { cx.set_global(ThemeRegistry::default()); ThemeSettings::register(cx); From ccccf84867454437e444605616615b117e1e9e2a Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:00:46 -0400 Subject: [PATCH 44/54] Define all color scales --- crates/theme2/src/default.rs | 1984 ++++++++++++++++++++++++++++++++++ 1 file changed, 1984 insertions(+) diff --git a/crates/theme2/src/default.rs b/crates/theme2/src/default.rs index d1550f9e78..cdfd135885 100644 --- a/crates/theme2/src/default.rs +++ b/crates/theme2/src/default.rs @@ -38,6 +38,812 @@ pub fn default_color_scales() -> ColorScales { HashMap::from_iter([(Red, red().into())]) } +fn gray() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Gray, + light: [ + "#fcfcfc00", + "#f9f9f900", + "#f0f0f000", + "#e8e8e800", + "#e0e0e000", + "#d9d9d900", + "#cecece00", + "#bbbbbb00", + "#8d8d8d00", + "#83838300", + "#64646400", + "#20202000", + ], + light_alpha: [ + "#00000003", + "#00000006", + "#0000000f", + "#00000017", + "#0000001f", + "#00000026", + "#00000031", + "#00000044", + "#00000072", + "#0000007c", + "#0000009b", + "#000000df", + ], + dark: [ + "#11111100", + "#19191900", + "#22222200", + "#2a2a2a00", + "#31313100", + "#3a3a3a00", + "#48484800", + "#60606000", + "#6e6e6e00", + "#7b7b7b00", + "#b4b4b400", + "#eeeeee00", + ], + dark_alpha: [ + "#00000000", + "#ffffff09", + "#ffffff12", + "#ffffff1b", + "#ffffff22", + "#ffffff2c", + "#ffffff3b", + "#ffffff55", + "#ffffff64", + "#ffffff72", + "#ffffffaf", + "#ffffffed", + ], + } +} + +fn mauve() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Mauve, + light: [ + "#fdfcfd00", + "#faf9fb00", + "#f2eff300", + "#eae7ec00", + "#e3dfe600", + "#dbd8e000", + "#d0cdd700", + "#bcbac700", + "#8e8c9900", + "#84828e00", + "#65636d00", + "#211f2600", + ], + light_alpha: [ + "#55005503", + "#2b005506", + "#30004010", + "#20003618", + "#20003820", + "#14003527", + "#10003332", + "#08003145", + "#05001d73", + "#0500197d", + "#0400119c", + "#020008e0", + ], + dark: [ + "#12111300", + "#1a191b00", + "#23222500", + "#2b292d00", + "#32303500", + "#3c393f00", + "#49474e00", + "#625f6900", + "#6f6d7800", + "#7c7a8500", + "#b5b2bc00", + "#eeeef000", + ], + dark_alpha: [ + "#00000000", + "#f5f4f609", + "#ebeaf814", + "#eee5f81d", + "#efe6fe25", + "#f1e6fd30", + "#eee9ff40", + "#eee7ff5d", + "#eae6fd6e", + "#ece9fd7c", + "#f5f1ffb7", + "#fdfdffef", + ], + } +} + +fn slate() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Slate, + light: [ + "#fcfcfd00", + "#f9f9fb00", + "#f0f0f300", + "#e8e8ec00", + "#e0e1e600", + "#d9d9e000", + "#cdced600", + "#b9bbc600", + "#8b8d9800", + "#80838d00", + "#60646c00", + "#1c202400", + ], + light_alpha: [ + "#00005503", + "#00005506", + "#0000330f", + "#00002d17", + "#0009321f", + "#00002f26", + "#00062e32", + "#00083046", + "#00051d74", + "#00071b7f", + "#0007149f", + "#000509e3", + ], + dark: [ + "#11111300", + "#18191b00", + "#21222500", + "#272a2d00", + "#2e313500", + "#363a3f00", + "#43484e00", + "#5a616900", + "#696e7700", + "#777b8400", + "#b0b4ba00", + "#edeef000", + ], + dark_alpha: [ + "#00000000", + "#d8f4f609", + "#ddeaf814", + "#d3edf81d", + "#d9edfe25", + "#d6ebfd30", + "#d9edff40", + "#d9edff5d", + "#dfebfd6d", + "#e5edfd7b", + "#f1f7feb5", + "#fcfdffef", + ], + } +} + +fn sage() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Sage, + light: [ + "#fbfdfc00", + "#f7f9f800", + "#eef1f000", + "#e6e9e800", + "#dfe2e000", + "#d7dad900", + "#cbcfcd00", + "#b8bcba00", + "#868e8b00", + "#7c848100", + "#5f656300", + "#1a211e00", + ], + light_alpha: [ + "#00804004", + "#00402008", + "#002d1e11", + "#001f1519", + "#00180820", + "#00140d28", + "#00140a34", + "#000f0847", + "#00110b79", + "#00100a83", + "#000a07a0", + "#000805e5", + ], + dark: [ + "#10121100", + "#17191800", + "#20222100", + "#272a2900", + "#2e313000", + "#373b3900", + "#44494700", + "#5b625f00", + "#63706b00", + "#717d7900", + "#adb5b200", + "#eceeed00", + ], + dark_alpha: [ + "#00000000", + "#f0f2f108", + "#f3f5f412", + "#f2fefd1a", + "#f1fbfa22", + "#edfbf42d", + "#edfcf73c", + "#ebfdf657", + "#dffdf266", + "#e5fdf674", + "#f4fefbb0", + "#fdfffeed", + ], + } +} + +fn olive() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Olive, + light: [ + "#fcfdfc00", + "#f8faf800", + "#eff1ef00", + "#e7e9e700", + "#dfe2df00", + "#d7dad700", + "#cccfcc00", + "#b9bcb800", + "#898e8700", + "#7f847d00", + "#60655f00", + "#1d211c00", + ], + light_alpha: [ + "#00550003", + "#00490007", + "#00200010", + "#00160018", + "#00180020", + "#00140028", + "#000f0033", + "#040f0047", + "#050f0078", + "#040e0082", + "#020a00a0", + "#010600e3", + ], + dark: [ + "#11121000", + "#18191700", + "#21222000", + "#282a2700", + "#2f312e00", + "#383a3600", + "#45484300", + "#5c625b00", + "#68706600", + "#767d7400", + "#afb5ad00", + "#eceeec00", + ], + dark_alpha: [ + "#00000000", + "#f1f2f008", + "#f4f5f312", + "#f3fef21a", + "#f2fbf122", + "#f4faed2c", + "#f2fced3b", + "#edfdeb57", + "#ebfde766", + "#f0fdec74", + "#f6fef4b0", + "#fdfffded", + ], + } +} + +fn sand() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Sand, + light: [ + "#fdfdfc00", + "#f9f9f800", + "#f1f0ef00", + "#e9e8e600", + "#e2e1de00", + "#dad9d600", + "#cfceca00", + "#bcbbb500", + "#8d8d8600", + "#82827c00", + "#63635e00", + "#21201c00", + ], + light_alpha: [ + "#55550003", + "#25250007", + "#20100010", + "#1f150019", + "#1f180021", + "#19130029", + "#19140035", + "#1915014a", + "#0f0f0079", + "#0c0c0083", + "#080800a1", + "#060500e3", + ], + dark: [ + "#11111000", + "#19191800", + "#22222100", + "#2a2a2800", + "#31312e00", + "#3b3a3700", + "#49484400", + "#62605b00", + "#6f6d6600", + "#7c7b7400", + "#b5b3ad00", + "#eeeeec00", + ], + dark_alpha: [ + "#00000000", + "#f4f4f309", + "#f6f6f513", + "#fefef31b", + "#fbfbeb23", + "#fffaed2d", + "#fffbed3c", + "#fff9eb57", + "#fffae965", + "#fffdee73", + "#fffcf4b0", + "#fffffded", + ], + } +} + +fn gold() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Gold, + light: [ + "#fdfdfc00", + "#faf9f200", + "#f2f0e700", + "#eae6db00", + "#e1dccf00", + "#d8d0bf00", + "#cbc0aa00", + "#b9a88d00", + "#97836500", + "#8c7a5e00", + "#71624b00", + "#3b352b00", + ], + light_alpha: [ + "#55550003", + "#9d8a000d", + "#75600018", + "#6b4e0024", + "#60460030", + "#64440040", + "#63420055", + "#633d0072", + "#5332009a", + "#492d00a1", + "#362100b4", + "#130c00d4", + ], + dark: [ + "#12121100", + "#1b1a1700", + "#24231f00", + "#2d2b2600", + "#38352e00", + "#44403900", + "#544f4600", + "#69625600", + "#97836500", + "#a3907300", + "#cbb99f00", + "#e8e2d900", + ], + dark_alpha: [ + "#91911102", + "#f9e29d0b", + "#f8ecbb15", + "#ffeec41e", + "#feecc22a", + "#feebcb37", + "#ffedcd48", + "#fdeaca5f", + "#ffdba690", + "#fedfb09d", + "#fee7c6c8", + "#fef7ede7", + ], + } +} + +fn bronze() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Bronze, + light: [ + "#fdfcfc00", + "#fdf7f500", + "#f6edea00", + "#efe4df00", + "#e7d9d300", + "#dfcdc500", + "#d3bcb300", + "#c2a49900", + "#a1807200", + "#95746800", + "#7d5e5400", + "#43302b00", + ], + light_alpha: [ + "#55000003", + "#cc33000a", + "#92250015", + "#80280020", + "#7423002c", + "#7324003a", + "#6c1f004c", + "#671c0066", + "#551a008d", + "#4c150097", + "#3d0f00ab", + "#1d0600d4", + ], + dark: [ + "#14111000", + "#1c191700", + "#26222000", + "#302a2700", + "#3b333000", + "#493e3a00", + "#5a4c4700", + "#6f5f5800", + "#a1807200", + "#ae8c7e00", + "#d4b3a500", + "#ede0d900", + ], + dark_alpha: [ + "#d1110004", + "#fbbc910c", + "#faceb817", + "#facdb622", + "#ffd2c12d", + "#ffd1c03c", + "#fdd0c04f", + "#ffd6c565", + "#fec7b09b", + "#fecab5a9", + "#ffd7c6d1", + "#fff1e9ec", + ], + } +} + +fn brown() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Brown, + light: [ + "#fefdfc00", + "#fcf9f600", + "#f6eee700", + "#f0e4d900", + "#ebdaca00", + "#e4cdb700", + "#dcbc9f00", + "#cea37e00", + "#ad7f5800", + "#a0755300", + "#815e4600", + "#3e332e00", + ], + light_alpha: [ + "#aa550003", + "#aa550009", + "#a04b0018", + "#9b4a0026", + "#9f4d0035", + "#a04e0048", + "#a34e0060", + "#9f4a0081", + "#823c00a7", + "#723300ac", + "#522100b9", + "#140600d1", + ], + dark: [ + "#12110f00", + "#1c181600", + "#28211d00", + "#32292200", + "#3e312800", + "#4d3c2f00", + "#614a3900", + "#7c5f4600", + "#ad7f5800", + "#b88c6700", + "#dbb59400", + "#f2e1ca00", + ], + dark_alpha: [ + "#91110002", + "#fba67c0c", + "#fcb58c19", + "#fbbb8a24", + "#fcb88931", + "#fdba8741", + "#ffbb8856", + "#ffbe8773", + "#feb87da8", + "#ffc18cb3", + "#fed1aad9", + "#feecd4f2", + ], + } +} + +fn yellow() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Yellow, + light: [ + "#fdfdf900", + "#fefce900", + "#fffab800", + "#fff39400", + "#ffe77000", + "#f3d76800", + "#e4c76700", + "#d5ae3900", + "#ffe62900", + "#ffdc0000", + "#9e6c0000", + "#473b1f00", + ], + light_alpha: [ + "#aaaa0006", + "#f4dd0016", + "#ffee0047", + "#ffe3016b", + "#ffd5008f", + "#ebbc0097", + "#d2a10098", + "#c99700c6", + "#ffe100d6", + "#ffdc0000", + "#9e6c0000", + "#2e2000e0", + ], + dark: [ + "#14120b00", + "#1b180f00", + "#2d230500", + "#362b0000", + "#43350000", + "#52420200", + "#66541700", + "#836a2100", + "#ffe62900", + "#ffff5700", + "#f5e14700", + "#f6eeb400", + ], + dark_alpha: [ + "#d1510004", + "#f9b4000b", + "#ffaa001e", + "#fdb70028", + "#febb0036", + "#fec40046", + "#fdcb225c", + "#fdca327b", + "#ffe62900", + "#ffff5700", + "#fee949f5", + "#fef6baf6", + ], + } +} + +fn amber() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Amber, + light: [ + "#fefdfb00", + "#fefbe900", + "#fff7c200", + "#ffee9c00", + "#fbe57700", + "#f3d67300", + "#e9c16200", + "#e2a33600", + "#ffc53d00", + "#ffba1800", + "#ab640000", + "#4f342200", + ], + light_alpha: [ + "#c0800004", + "#f4d10016", + "#ffde003d", + "#ffd40063", + "#f8cf0088", + "#eab5008c", + "#dc9b009d", + "#da8a00c9", + "#ffb300c2", + "#ffb300e7", + "#ab640000", + "#341500dd", + ], + dark: [ + "#16120c00", + "#1d180f00", + "#30200800", + "#3f270000", + "#4d300000", + "#5c3d0500", + "#714f1900", + "#8f642400", + "#ffc53d00", + "#ffd60a00", + "#ffca1600", + "#ffe7b300", + ], + dark_alpha: [ + "#e63c0006", + "#fd9b000d", + "#fa820022", + "#fc820032", + "#fd8b0041", + "#fd9b0051", + "#ffab2567", + "#ffae3587", + "#ffc53d00", + "#ffd60a00", + "#ffca1600", + "#ffe7b300", + ], + } +} + +fn orange() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Orange, + light: [ + "#fefcfb00", + "#fff7ed00", + "#ffefd600", + "#ffdfb500", + "#ffd19a00", + "#ffc18200", + "#f5ae7300", + "#ec945500", + "#f76b1500", + "#ef5f0000", + "#cc4e0000", + "#582d1d00", + ], + light_alpha: [ + "#c0400004", + "#ff8e0012", + "#ff9c0029", + "#ff91014a", + "#ff8b0065", + "#ff81007d", + "#ed6c008c", + "#e35f00aa", + "#f65e00ea", + "#ef5f0000", + "#cc4e0000", + "#431200e2", + ], + dark: [ + "#17120e00", + "#1e160f00", + "#331e0b00", + "#46210000", + "#56280000", + "#66350c00", + "#7e451d00", + "#a3582900", + "#f76b1500", + "#ff801f00", + "#ffa05700", + "#ffe0c200", + ], + dark_alpha: [ + "#ec360007", + "#fe6d000e", + "#fb6a0025", + "#ff590039", + "#ff61004a", + "#fd75045c", + "#ff832c75", + "#fe84389d", + "#fe6d15f7", + "#ff801f00", + "#ffa05700", + "#ffe0c200", + ], + } +} + +fn tomato() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Tomato, + light: [ + "#fffcfc00", + "#fff8f700", + "#feebe700", + "#ffdcd300", + "#ffcdc200", + "#fdbdaf00", + "#f5a89800", + "#ec8e7b00", + "#e54d2e00", + "#dd442500", + "#d1341500", + "#5c271f00", + ], + light_alpha: [ + "#ff000003", + "#ff200008", + "#f52b0018", + "#ff35002c", + "#ff2e003d", + "#f92d0050", + "#e7280067", + "#db250084", + "#df2600d1", + "#d72400da", + "#cd2200ea", + "#460900e0", + ], + dark: [ + "#18111100", + "#1f151300", + "#39171400", + "#4e151100", + "#5e1c1600", + "#6e292000", + "#853a2d00", + "#ac4d3900", + "#e54d2e00", + "#ec614200", + "#ff977d00", + "#fbd3cb00", + ], + dark_alpha: [ + "#f1121208", + "#ff55330f", + "#ff35232b", + "#fd201142", + "#fe332153", + "#ff4f3864", + "#fd644a7d", + "#fe6d4ea7", + "#fe5431e4", + "#ff6847eb", + "#ff977d00", + "#ffd6cefb", + ], + } +} + fn red() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Red, @@ -99,3 +905,1181 @@ fn red() -> DefaultColorScaleSet { ], } } + +fn ruby() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Ruby, + light: [ + "#fffcfd00", + "#fff7f800", + "#feeaed00", + "#ffdce100", + "#ffced600", + "#f8bfc800", + "#efacb800", + "#e592a300", + "#e5466600", + "#dc3b5d00", + "#ca244d00", + "#64172b00", + ], + light_alpha: [ + "#ff005503", + "#ff002008", + "#f3002515", + "#ff002523", + "#ff002a31", + "#e4002440", + "#ce002553", + "#c300286d", + "#db002cb9", + "#d2002cc4", + "#c10030db", + "#550016e8", + ], + dark: [ + "#19111300", + "#1e151700", + "#3a141e00", + "#4e132500", + "#5e1a2e00", + "#6f253900", + "#88344700", + "#b3445a00", + "#e5466600", + "#ec5a7200", + "#ff949d00", + "#fed2e100", + ], + dark_alpha: [ + "#f4124a09", + "#fe5a7f0e", + "#ff235d2c", + "#fd195e42", + "#fe2d6b53", + "#ff447665", + "#ff577d80", + "#ff5c7cae", + "#fe4c70e4", + "#ff617beb", + "#ff949d00", + "#ffd3e2fe", + ], + } +} + +fn crimson() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Crimson, + light: [ + "#fffcfd00", + "#fef7f900", + "#ffe9f000", + "#fedce700", + "#facedd00", + "#f3bed100", + "#eaacc300", + "#e093b200", + "#e93d8200", + "#df347800", + "#cb1d6300", + "#62163900", + ], + light_alpha: [ + "#ff005503", + "#e0004008", + "#ff005216", + "#f8005123", + "#e5004f31", + "#d0004b41", + "#bf004753", + "#b6004a6c", + "#e2005bc2", + "#d70056cb", + "#c4004fe2", + "#530026e9", + ], + dark: [ + "#19111400", + "#20131800", + "#38152500", + "#4d122f00", + "#5c183900", + "#6d254500", + "#87335600", + "#b0436e00", + "#e93d8200", + "#ee518a00", + "#ff92ad00", + "#fdd3e800", + ], + dark_alpha: [ + "#f4126709", + "#f22f7a11", + "#fe2a8b2a", + "#fd158741", + "#fd278f51", + "#fe459763", + "#fd559b7f", + "#fe5b9bab", + "#fe418de8", + "#ff5693ed", + "#ff92ad00", + "#ffd5eafd", + ], + } +} + +fn pink() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Pink, + light: [ + "#fffcfe00", + "#fef7fb00", + "#fee9f500", + "#fbdcef00", + "#f6cee700", + "#efbfdd00", + "#e7acd000", + "#dd93c200", + "#d6409f00", + "#cf389700", + "#c2298a00", + "#65124900", + ], + light_alpha: [ + "#ff00aa03", + "#e0008008", + "#f4008c16", + "#e2008b23", + "#d1008331", + "#c0007840", + "#b6006f53", + "#af006f6c", + "#c8007fbf", + "#c2007ac7", + "#b60074d6", + "#59003bed", + ], + dark: [ + "#19111700", + "#21121d00", + "#37172f00", + "#4b143d00", + "#591c4700", + "#69295500", + "#83386900", + "#a8488500", + "#d6409f00", + "#de51a800", + "#ff8dcc00", + "#fdd1ea00", + ], + dark_alpha: [ + "#f412bc09", + "#f420bb12", + "#fe37cc29", + "#fc1ec43f", + "#fd35c24e", + "#fd51c75f", + "#fd62c87b", + "#ff68c8a2", + "#fe49bcd4", + "#ff5cc0dc", + "#ff8dcc00", + "#ffd3ecfd", + ], + } +} + +fn plum() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Plum, + light: [ + "#fefcff00", + "#fdf7fd00", + "#fbebfb00", + "#f7def800", + "#f2d1f300", + "#e9c2ec00", + "#deade300", + "#cf91d800", + "#ab4aba00", + "#a144af00", + "#953ea300", + "#53195d00", + ], + light_alpha: [ + "#aa00ff03", + "#c000c008", + "#cc00cc14", + "#c200c921", + "#b700bd2e", + "#a400b03d", + "#9900a852", + "#9000a56e", + "#89009eb5", + "#7f0092bb", + "#730086c1", + "#40004be6", + ], + dark: [ + "#18111800", + "#20132000", + "#351a3500", + "#451d4700", + "#51245400", + "#5e306100", + "#73407900", + "#92549c00", + "#ab4aba00", + "#b658c400", + "#e796f300", + "#f4d4f400", + ], + dark_alpha: [ + "#f112f108", + "#f22ff211", + "#fd4cfd27", + "#f646ff3a", + "#f455ff48", + "#f66dff56", + "#f07cfd70", + "#ee84ff95", + "#e961feb6", + "#ed70ffc0", + "#f19cfef3", + "#feddfef4", + ], + } +} + +fn purple() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Purple, + light: [ + "#fefcfe00", + "#fbf7fe00", + "#f7edfe00", + "#f2e2fc00", + "#ead5f900", + "#e0c4f400", + "#d1afec00", + "#be93e400", + "#8e4ec600", + "#8347b900", + "#8145b500", + "#40206000", + ], + light_alpha: [ + "#aa00aa03", + "#8000e008", + "#8e00f112", + "#8d00e51d", + "#8000db2a", + "#7a01d03b", + "#6d00c350", + "#6600c06c", + "#5c00adb1", + "#53009eb8", + "#52009aba", + "#250049df", + ], + dark: [ + "#18111b00", + "#1e152300", + "#301c3b00", + "#3d224e00", + "#48295c00", + "#54346b00", + "#66428200", + "#8457aa00", + "#8e4ec600", + "#9a5cd000", + "#d19dff00", + "#ecd9fa00", + ], + dark_alpha: [ + "#b412f90b", + "#b744f714", + "#c150ff2d", + "#bb53fd42", + "#be5cfd51", + "#c16dfd61", + "#c378fd7a", + "#c47effa4", + "#b661ffc2", + "#bc6fffcd", + "#d19dff00", + "#f1ddfffa", + ], + } +} + +fn violet() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Violet, + light: [ + "#fdfcfe00", + "#faf8ff00", + "#f4f0fe00", + "#ebe4ff00", + "#e1d9ff00", + "#d4cafe00", + "#c2b5f500", + "#aa99ec00", + "#6e56cf00", + "#654dc400", + "#6550b900", + "#2f265f00", + ], + light_alpha: [ + "#5500aa03", + "#4900ff07", + "#4400ee0f", + "#4300ff1b", + "#3600ff26", + "#3100fb35", + "#2d01dd4a", + "#2b00d066", + "#2400b7a9", + "#2300abb2", + "#1f0099af", + "#0b0043d9", + ], + dark: [ + "#14121f00", + "#1b152500", + "#291f4300", + "#33255b00", + "#3c2e6900", + "#47387600", + "#56468b00", + "#6958ad00", + "#6e56cf00", + "#7d66d900", + "#baa7ff00", + "#e2ddfe00", + ], + dark_alpha: [ + "#4422ff0f", + "#853ff916", + "#8354fe36", + "#7d51fd50", + "#845ffd5f", + "#8f6cfd6d", + "#9879ff83", + "#977dfea8", + "#8668ffcc", + "#9176fed7", + "#baa7ff00", + "#e3defffe", + ], + } +} + +fn iris() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Iris, + light: [ + "#fdfdff00", + "#f8f8ff00", + "#f0f1fe00", + "#e6e7ff00", + "#dadcff00", + "#cbcdff00", + "#b8baf800", + "#9b9ef000", + "#5b5bd600", + "#5151cd00", + "#5753c600", + "#27296200", + ], + light_alpha: [ + "#0000ff02", + "#0000ff07", + "#0011ee0f", + "#000bff19", + "#000eff25", + "#000aff34", + "#0008e647", + "#0008d964", + "#0000c0a4", + "#0000b6ae", + "#0600abac", + "#000246d8", + ], + dark: [ + "#13131e00", + "#17162500", + "#20224800", + "#262a6500", + "#30337400", + "#3d3e8200", + "#4a4a9500", + "#5958b100", + "#5b5bd600", + "#6e6ade00", + "#b1a9ff00", + "#e0dffe00", + ], + dark_alpha: [ + "#3636fe0e", + "#564bf916", + "#525bff3b", + "#4d58ff5a", + "#5b62fd6b", + "#6d6ffd7a", + "#7777fe8e", + "#7b7afeac", + "#6a6afed4", + "#7d79ffdc", + "#b1a9ff00", + "#e1e0fffe", + ], + } +} + +fn indigo() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Indigo, + light: [ + "#fdfdfe00", + "#f7f9ff00", + "#edf2fe00", + "#e1e9ff00", + "#d2deff00", + "#c1d0ff00", + "#abbdf900", + "#8da4ef00", + "#3e63dd00", + "#3358d400", + "#3a5bc700", + "#1f2d5c00", + ], + light_alpha: [ + "#00008002", + "#0040ff08", + "#0047f112", + "#0044ff1e", + "#0044ff2d", + "#003eff3e", + "#0037ed54", + "#0034dc72", + "#0031d2c1", + "#002ec9cc", + "#002bb7c5", + "#001046e0", + ], + dark: [ + "#11131f00", + "#14172600", + "#18244900", + "#1d2e6200", + "#25397400", + "#30438400", + "#3a4f9700", + "#435db100", + "#3e63dd00", + "#5472e400", + "#9eb1ff00", + "#d6e1ff00", + ], + dark_alpha: [ + "#1133ff0f", + "#3354fa17", + "#2f62ff3c", + "#3566ff57", + "#4171fd6b", + "#5178fd7c", + "#5a7fff90", + "#5b81feac", + "#4671ffdb", + "#5c7efee3", + "#9eb1ff00", + "#d6e1ff00", + ], + } +} + +fn blue() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Blue, + light: [ + "#fbfdff00", + "#f4faff00", + "#e6f4fe00", + "#d5efff00", + "#c2e5ff00", + "#acd8fc00", + "#8ec8f600", + "#5eb1ef00", + "#0090ff00", + "#0588f000", + "#0d74ce00", + "#11326400", + ], + light_alpha: [ + "#0080ff04", + "#008cff0b", + "#008ff519", + "#009eff2a", + "#0093ff3d", + "#0088f653", + "#0083eb71", + "#0084e6a1", + "#0090ff00", + "#0086f0fa", + "#006dcbf2", + "#002359ee", + ], + dark: [ + "#0d152000", + "#11192700", + "#0d284700", + "#00336200", + "#00407400", + "#104d8700", + "#205d9e00", + "#2870bd00", + "#0090ff00", + "#3b9eff00", + "#70b8ff00", + "#c2e6ff00", + ], + dark_alpha: [ + "#004df211", + "#1166fb18", + "#0077ff3a", + "#0075ff57", + "#0081fd6b", + "#0f89fd7f", + "#2a91fe98", + "#3094feb9", + "#0090ff00", + "#3b9eff00", + "#70b8ff00", + "#c2e6ff00", + ], + } +} + +fn cyan() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Cyan, + light: [ + "#fafdfe00", + "#f2fafb00", + "#def7f900", + "#caf1f600", + "#b5e9f000", + "#9ddde700", + "#7dcedc00", + "#3db9cf00", + "#00a2c700", + "#0797b900", + "#107d9800", + "#0d3c4800", + ], + light_alpha: [ + "#0099cc05", + "#009db10d", + "#00c2d121", + "#00bcd435", + "#01b4cc4a", + "#00a7c162", + "#009fbb82", + "#00a3c0c2", + "#00a2c700", + "#0094b7f8", + "#007491ef", + "#00323ef2", + ], + dark: [ + "#0b161a00", + "#101b2000", + "#082c3600", + "#00384800", + "#00455800", + "#04546800", + "#12677e00", + "#11809c00", + "#00a2c700", + "#23afd000", + "#4ccce600", + "#b6ecf700", + ], + dark_alpha: [ + "#0091f70a", + "#02a7f211", + "#00befd28", + "#00baff3b", + "#00befd4d", + "#00c7fd5e", + "#14cdff75", + "#11cfff95", + "#00cfffc3", + "#28d6ffcd", + "#52e1fee5", + "#bbf3fef7", + ], + } +} + +fn teal() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Teal, + light: [ + "#fafefd00", + "#f3fbf900", + "#e0f8f300", + "#ccf3ea00", + "#b8eae000", + "#a1ded200", + "#83cdc100", + "#53b9ab00", + "#12a59400", + "#0d9b8a00", + "#00857300", + "#0d3d3800", + ], + light_alpha: [ + "#00cc9905", + "#00aa800c", + "#00c69d1f", + "#00c39633", + "#00b49047", + "#00a6855e", + "#0099807c", + "#009783ac", + "#009e8ced", + "#009684f2", + "#00857300", + "#00332df2", + ], + dark: [ + "#0d151400", + "#111c1b00", + "#0d2d2a00", + "#023b3700", + "#08484300", + "#14575000", + "#1c696100", + "#207e7300", + "#12a59400", + "#0eb39e00", + "#0bd8b600", + "#adf0dd00", + ], + dark_alpha: [ + "#00deab05", + "#12fbe60c", + "#00ffe61e", + "#00ffe92d", + "#00ffea3b", + "#1cffe84b", + "#2efde85f", + "#32ffe775", + "#13ffe49f", + "#0dffe0ae", + "#0afed5d6", + "#b8ffebef", + ], + } +} + +fn jade() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Jade, + light: [ + "#fbfefd00", + "#f4fbf700", + "#e6f7ed00", + "#d6f1e300", + "#c3e9d700", + "#acdec800", + "#8bceb600", + "#56ba9f00", + "#29a38300", + "#26997b00", + "#20836800", + "#1d3b3100", + ], + light_alpha: [ + "#00c08004", + "#00a3460b", + "#00ae4819", + "#00a85129", + "#00a2553c", + "#009a5753", + "#00945f74", + "#00976ea9", + "#00916bd6", + "#008764d9", + "#007152df", + "#002217e2", + ], + dark: [ + "#0d151200", + "#121c1800", + "#0f2e2200", + "#0b3b2c00", + "#11483700", + "#1b574500", + "#24685400", + "#2a7e6800", + "#29a38300", + "#27b08b00", + "#1fd8a400", + "#adf0d400", + ], + dark_alpha: [ + "#00de4505", + "#27fba60c", + "#02f99920", + "#00ffaa2d", + "#11ffb63b", + "#34ffc24b", + "#45fdc75e", + "#48ffcf75", + "#38feca9d", + "#31fec7ab", + "#21fec0d6", + "#b8ffe1ef", + ], + } +} + +fn green() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Green, + light: [ + "#fbfefc00", + "#f4fbf600", + "#e6f6eb00", + "#d6f1df00", + "#c4e8d100", + "#adddc000", + "#8eceaa00", + "#5bb98b00", + "#30a46c00", + "#2b9a6600", + "#21835800", + "#193b2d00", + ], + light_alpha: [ + "#00c04004", + "#00a32f0b", + "#00a43319", + "#00a83829", + "#019c393b", + "#00963c52", + "#00914071", + "#00924ba4", + "#008f4acf", + "#008647d4", + "#00713fde", + "#002616e6", + ], + dark: [ + "#0e151200", + "#121b1700", + "#132d2100", + "#113b2900", + "#17493300", + "#20573e00", + "#28684a00", + "#2f7c5700", + "#30a46c00", + "#33b07400", + "#3dd68c00", + "#b1f1cb00", + ], + dark_alpha: [ + "#00de4505", + "#29f99d0b", + "#22ff991e", + "#11ff992d", + "#2bffa23c", + "#44ffaa4b", + "#50fdac5e", + "#54ffad73", + "#44ffa49e", + "#43fea4ab", + "#46fea5d4", + "#bbffd7f0", + ], + } +} + +fn grass() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Grass, + light: [ + "#fbfefb00", + "#f5fbf500", + "#e9f6e900", + "#daf1db00", + "#c9e8ca00", + "#b2ddb500", + "#94ce9a00", + "#65ba7400", + "#46a75800", + "#3e9b4f00", + "#2a7e3b00", + "#203c2500", + ], + light_alpha: [ + "#00c00004", + "#0099000a", + "#00970016", + "#009f0725", + "#00930536", + "#008f0a4d", + "#018b0f6b", + "#008d199a", + "#008619b9", + "#007b17c1", + "#006514d5", + "#002006df", + ], + dark: [ + "#0e151100", + "#141a1500", + "#1b2a1e00", + "#1d3a2400", + "#25482d00", + "#2d573600", + "#36674000", + "#3e794900", + "#46a75800", + "#53b36500", + "#71d08300", + "#c2f0c200", + ], + dark_alpha: [ + "#00de1205", + "#5ef7780a", + "#70fe8c1b", + "#57ff802c", + "#68ff8b3b", + "#71ff8f4b", + "#77fd925d", + "#77fd9070", + "#65ff82a1", + "#72ff8dae", + "#89ff9fcd", + "#ceffceef", + ], + } +} + +fn lime() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Lime, + light: [ + "#fcfdfa00", + "#f8faf300", + "#eef6d600", + "#e2f0bd00", + "#d3e7a600", + "#c2da9100", + "#abc97800", + "#8db65400", + "#bdee6300", + "#b0e64c00", + "#5c7c2f00", + "#37401c00", + ], + light_alpha: [ + "#66990005", + "#6b95000c", + "#96c80029", + "#8fc60042", + "#81bb0059", + "#72aa006e", + "#61990087", + "#559200ab", + "#93e4009c", + "#8fdc00b3", + "#375f00d0", + "#1e2900e3", + ], + dark: [ + "#11130c00", + "#151a1000", + "#1f291700", + "#29371d00", + "#33442300", + "#3d522a00", + "#49623100", + "#57753800", + "#bdee6300", + "#d4ff7000", + "#bde56c00", + "#e3f7ba00", + ], + dark_alpha: [ + "#11bb0003", + "#78f7000a", + "#9bfd4c1a", + "#a7fe5c29", + "#affe6537", + "#b2fe6d46", + "#b6ff6f57", + "#b6fd6d6c", + "#caff69ed", + "#d4ff7000", + "#d1fe77e4", + "#e9febff7", + ], + } +} + +fn mint() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Mint, + light: [ + "#f9fefd00", + "#f2fbf900", + "#ddf9f200", + "#c8f4e900", + "#b3ecde00", + "#9ce0d000", + "#7ecfbd00", + "#4cbba500", + "#86ead400", + "#7de0cb00", + "#02786400", + "#16433c00", + ], + light_alpha: [ + "#00d5aa06", + "#00b18a0d", + "#00d29e22", + "#00cc9937", + "#00c0914c", + "#00b08663", + "#00a17d81", + "#009e7fb3", + "#00d3a579", + "#00c39982", + "#007763fd", + "#00312ae9", + ], + dark: [ + "#0e151500", + "#0f1b1b00", + "#092c2b00", + "#003a3800", + "#00474400", + "#10565000", + "#1e685f00", + "#277f7000", + "#86ead400", + "#a8f5e500", + "#58d5ba00", + "#c4f5e100", + ], + dark_alpha: [ + "#00dede05", + "#00f9f90b", + "#00fff61d", + "#00fff42c", + "#00fff23a", + "#0effeb4a", + "#34fde55e", + "#41ffdf76", + "#92ffe7e9", + "#aefeedf5", + "#67ffded2", + "#cbfee9f5", + ], + } +} + +fn sky() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Sky, + light: [ + "#f9feff00", + "#f1fafd00", + "#e1f6fd00", + "#d1f0fa00", + "#bee7f500", + "#a9daed00", + "#8dcae300", + "#60b3d700", + "#7ce2fe00", + "#74daf800", + "#00749e00", + "#1d3e5600", + ], + light_alpha: [ + "#00d5ff06", + "#00a4db0e", + "#00b3ee1e", + "#00ace42e", + "#00a1d841", + "#0092ca56", + "#0089c172", + "#0085bf9f", + "#00c7fe83", + "#00bcf38b", + "#00749e00", + "#002540e2", + ], + dark: [ + "#0d141f00", + "#111a2700", + "#11284000", + "#11355500", + "#15446700", + "#1b537b00", + "#1f669200", + "#197cae00", + "#7ce2fe00", + "#a8eeff00", + "#75c7f000", + "#c2f3ff00", + ], + dark_alpha: [ + "#0044ff0f", + "#1171fb18", + "#1184fc33", + "#128fff49", + "#1c9dfd5d", + "#28a5ff72", + "#2badfe8b", + "#1db2fea9", + "#7ce3fffe", + "#a8eeff00", + "#7cd3ffef", + "#c2f3ff00", + ], + } +} + +fn black() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::Black, + light: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + light_alpha: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + dark: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + dark_alpha: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + } +} + +fn white() -> DefaultColorScaleSet { + DefaultColorScaleSet { + scale: ColorScaleName::White, + light: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + light_alpha: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + dark: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + dark_alpha: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + } +} From b128377cd2459659ec4e6d3bd102b03cbd82cad4 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:03:33 -0400 Subject: [PATCH 45/54] Register all of the color scales --- crates/theme2/src/default.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/crates/theme2/src/default.rs b/crates/theme2/src/default.rs index cdfd135885..5aa569325a 100644 --- a/crates/theme2/src/default.rs +++ b/crates/theme2/src/default.rs @@ -35,7 +35,41 @@ impl From for ColorScaleSet { pub fn default_color_scales() -> ColorScales { use ColorScaleName::*; - HashMap::from_iter([(Red, red().into())]) + HashMap::from_iter([ + (Gray, gray().into()), + (Mauve, mauve().into()), + (Slate, slate().into()), + (Sage, sage().into()), + (Olive, olive().into()), + (Sand, sand().into()), + (Gold, gold().into()), + (Bronze, bronze().into()), + (Brown, brown().into()), + (Yellow, yellow().into()), + (Amber, amber().into()), + (Orange, orange().into()), + (Tomato, tomato().into()), + (Red, red().into()), + (Ruby, ruby().into()), + (Crimson, crimson().into()), + (Pink, pink().into()), + (Plum, plum().into()), + (Purple, purple().into()), + (Violet, violet().into()), + (Iris, iris().into()), + (Indigo, indigo().into()), + (Blue, blue().into()), + (Cyan, cyan().into()), + (Teal, teal().into()), + (Jade, jade().into()), + (Green, green().into()), + (Grass, grass().into()), + (Lime, lime().into()), + (Mint, mint().into()), + (Sky, sky().into()), + (Black, black().into()), + (White, white().into()), + ]) } fn gray() -> DefaultColorScaleSet { From e0f68c77b02cb3b144761075ddb959260f79a7b3 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:08:37 -0400 Subject: [PATCH 46/54] Add type alias for steps in color scales --- crates/theme2/src/scale.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs index a7bd6a5e22..3aea476228 100644 --- a/crates/theme2/src/scale.rs +++ b/crates/theme2/src/scale.rs @@ -89,6 +89,9 @@ pub type ColorScale = [Hsla; 12]; pub type ColorScales = HashMap; +/// A one-based step in a [`ColorScale`]. +pub type ColorScaleStep = usize; + pub struct ColorScaleSet { name: ColorScaleName, light: ColorScale, @@ -118,19 +121,19 @@ impl ColorScaleSet { self.name.to_string() } - pub fn light(&self, step: usize) -> Hsla { + pub fn light(&self, step: ColorScaleStep) -> Hsla { self.light[step - 1] } - pub fn light_alpha(&self, step: usize) -> Hsla { + pub fn light_alpha(&self, step: ColorScaleStep) -> Hsla { self.light_alpha[step - 1] } - pub fn dark(&self, step: usize) -> Hsla { + pub fn dark(&self, step: ColorScaleStep) -> Hsla { self.dark[step - 1] } - pub fn dark_alpha(&self, step: usize) -> Hsla { + pub fn dark_alpha(&self, step: ColorScaleStep) -> Hsla { self.dark[step - 1] } @@ -143,24 +146,20 @@ impl ColorScaleSet { } } - /// Returns the one-based step in the scale. - /// - /// We usually reference steps as 1-12 instead of 0-11, so we - /// automatically subtract 1 from the index. - pub fn step(self, cx: &AppContext, index: usize) -> Hsla { + pub fn step(self, cx: &AppContext, step: ColorScaleStep) -> Hsla { let appearance = Self::current_appearance(cx); match appearance { - Appearance::Light => self.light(index), - Appearance::Dark => self.dark(index), + Appearance::Light => self.light(step), + Appearance::Dark => self.dark(step), } } - pub fn step_alpha(self, cx: &AppContext, index: usize) -> Hsla { + pub fn step_alpha(self, cx: &AppContext, step: ColorScaleStep) -> Hsla { let appearance = Self::current_appearance(cx); match appearance { - Appearance::Light => self.light_alpha(index), - Appearance::Dark => self.dark_alpha(index), + Appearance::Light => self.light_alpha(step), + Appearance::Dark => self.dark_alpha(step), } } } From 991f58409e441130dd2678c3835e8b536b805046 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:20:43 -0400 Subject: [PATCH 47/54] Fix alpha channel values for color scales --- crates/theme2/src/default.rs | 1560 +++++++++++++++++----------------- 1 file changed, 780 insertions(+), 780 deletions(-) diff --git a/crates/theme2/src/default.rs b/crates/theme2/src/default.rs index 5aa569325a..e3a0527f11 100644 --- a/crates/theme2/src/default.rs +++ b/crates/theme2/src/default.rs @@ -76,18 +76,18 @@ fn gray() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Gray, light: [ - "#fcfcfc00", - "#f9f9f900", - "#f0f0f000", - "#e8e8e800", - "#e0e0e000", - "#d9d9d900", - "#cecece00", - "#bbbbbb00", - "#8d8d8d00", - "#83838300", - "#64646400", - "#20202000", + "#fcfcfcff", + "#f9f9f9ff", + "#f0f0f0ff", + "#e8e8e8ff", + "#e0e0e0ff", + "#d9d9d9ff", + "#cececeff", + "#bbbbbbff", + "#8d8d8dff", + "#838383ff", + "#646464ff", + "#202020ff", ], light_alpha: [ "#00000003", @@ -104,18 +104,18 @@ fn gray() -> DefaultColorScaleSet { "#000000df", ], dark: [ - "#11111100", - "#19191900", - "#22222200", - "#2a2a2a00", - "#31313100", - "#3a3a3a00", - "#48484800", - "#60606000", - "#6e6e6e00", - "#7b7b7b00", - "#b4b4b400", - "#eeeeee00", + "#111111ff", + "#191919ff", + "#222222ff", + "#2a2a2aff", + "#313131ff", + "#3a3a3aff", + "#484848ff", + "#606060ff", + "#6e6e6eff", + "#7b7b7bff", + "#b4b4b4ff", + "#eeeeeeff", ], dark_alpha: [ "#00000000", @@ -138,18 +138,18 @@ fn mauve() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Mauve, light: [ - "#fdfcfd00", - "#faf9fb00", - "#f2eff300", - "#eae7ec00", - "#e3dfe600", - "#dbd8e000", - "#d0cdd700", - "#bcbac700", - "#8e8c9900", - "#84828e00", - "#65636d00", - "#211f2600", + "#fdfcfdff", + "#faf9fbff", + "#f2eff3ff", + "#eae7ecff", + "#e3dfe6ff", + "#dbd8e0ff", + "#d0cdd7ff", + "#bcbac7ff", + "#8e8c99ff", + "#84828eff", + "#65636dff", + "#211f26ff", ], light_alpha: [ "#55005503", @@ -166,18 +166,18 @@ fn mauve() -> DefaultColorScaleSet { "#020008e0", ], dark: [ - "#12111300", - "#1a191b00", - "#23222500", - "#2b292d00", - "#32303500", - "#3c393f00", - "#49474e00", - "#625f6900", - "#6f6d7800", - "#7c7a8500", - "#b5b2bc00", - "#eeeef000", + "#121113ff", + "#1a191bff", + "#232225ff", + "#2b292dff", + "#323035ff", + "#3c393fff", + "#49474eff", + "#625f69ff", + "#6f6d78ff", + "#7c7a85ff", + "#b5b2bcff", + "#eeeef0ff", ], dark_alpha: [ "#00000000", @@ -200,18 +200,18 @@ fn slate() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Slate, light: [ - "#fcfcfd00", - "#f9f9fb00", - "#f0f0f300", - "#e8e8ec00", - "#e0e1e600", - "#d9d9e000", - "#cdced600", - "#b9bbc600", - "#8b8d9800", - "#80838d00", - "#60646c00", - "#1c202400", + "#fcfcfdff", + "#f9f9fbff", + "#f0f0f3ff", + "#e8e8ecff", + "#e0e1e6ff", + "#d9d9e0ff", + "#cdced6ff", + "#b9bbc6ff", + "#8b8d98ff", + "#80838dff", + "#60646cff", + "#1c2024ff", ], light_alpha: [ "#00005503", @@ -228,18 +228,18 @@ fn slate() -> DefaultColorScaleSet { "#000509e3", ], dark: [ - "#11111300", - "#18191b00", - "#21222500", - "#272a2d00", - "#2e313500", - "#363a3f00", - "#43484e00", - "#5a616900", - "#696e7700", - "#777b8400", - "#b0b4ba00", - "#edeef000", + "#111113ff", + "#18191bff", + "#212225ff", + "#272a2dff", + "#2e3135ff", + "#363a3fff", + "#43484eff", + "#5a6169ff", + "#696e77ff", + "#777b84ff", + "#b0b4baff", + "#edeef0ff", ], dark_alpha: [ "#00000000", @@ -262,18 +262,18 @@ fn sage() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Sage, light: [ - "#fbfdfc00", - "#f7f9f800", - "#eef1f000", - "#e6e9e800", - "#dfe2e000", - "#d7dad900", - "#cbcfcd00", - "#b8bcba00", - "#868e8b00", - "#7c848100", - "#5f656300", - "#1a211e00", + "#fbfdfcff", + "#f7f9f8ff", + "#eef1f0ff", + "#e6e9e8ff", + "#dfe2e0ff", + "#d7dad9ff", + "#cbcfcdff", + "#b8bcbaff", + "#868e8bff", + "#7c8481ff", + "#5f6563ff", + "#1a211eff", ], light_alpha: [ "#00804004", @@ -290,18 +290,18 @@ fn sage() -> DefaultColorScaleSet { "#000805e5", ], dark: [ - "#10121100", - "#17191800", - "#20222100", - "#272a2900", - "#2e313000", - "#373b3900", - "#44494700", - "#5b625f00", - "#63706b00", - "#717d7900", - "#adb5b200", - "#eceeed00", + "#101211ff", + "#171918ff", + "#202221ff", + "#272a29ff", + "#2e3130ff", + "#373b39ff", + "#444947ff", + "#5b625fff", + "#63706bff", + "#717d79ff", + "#adb5b2ff", + "#eceeedff", ], dark_alpha: [ "#00000000", @@ -324,18 +324,18 @@ fn olive() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Olive, light: [ - "#fcfdfc00", - "#f8faf800", - "#eff1ef00", - "#e7e9e700", - "#dfe2df00", - "#d7dad700", - "#cccfcc00", - "#b9bcb800", - "#898e8700", - "#7f847d00", - "#60655f00", - "#1d211c00", + "#fcfdfcff", + "#f8faf8ff", + "#eff1efff", + "#e7e9e7ff", + "#dfe2dfff", + "#d7dad7ff", + "#cccfccff", + "#b9bcb8ff", + "#898e87ff", + "#7f847dff", + "#60655fff", + "#1d211cff", ], light_alpha: [ "#00550003", @@ -352,18 +352,18 @@ fn olive() -> DefaultColorScaleSet { "#010600e3", ], dark: [ - "#11121000", - "#18191700", - "#21222000", - "#282a2700", - "#2f312e00", - "#383a3600", - "#45484300", - "#5c625b00", - "#68706600", - "#767d7400", - "#afb5ad00", - "#eceeec00", + "#111210ff", + "#181917ff", + "#212220ff", + "#282a27ff", + "#2f312eff", + "#383a36ff", + "#454843ff", + "#5c625bff", + "#687066ff", + "#767d74ff", + "#afb5adff", + "#eceeecff", ], dark_alpha: [ "#00000000", @@ -386,18 +386,18 @@ fn sand() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Sand, light: [ - "#fdfdfc00", - "#f9f9f800", - "#f1f0ef00", - "#e9e8e600", - "#e2e1de00", - "#dad9d600", - "#cfceca00", - "#bcbbb500", - "#8d8d8600", - "#82827c00", - "#63635e00", - "#21201c00", + "#fdfdfcff", + "#f9f9f8ff", + "#f1f0efff", + "#e9e8e6ff", + "#e2e1deff", + "#dad9d6ff", + "#cfcecaff", + "#bcbbb5ff", + "#8d8d86ff", + "#82827cff", + "#63635eff", + "#21201cff", ], light_alpha: [ "#55550003", @@ -414,18 +414,18 @@ fn sand() -> DefaultColorScaleSet { "#060500e3", ], dark: [ - "#11111000", - "#19191800", - "#22222100", - "#2a2a2800", - "#31312e00", - "#3b3a3700", - "#49484400", - "#62605b00", - "#6f6d6600", - "#7c7b7400", - "#b5b3ad00", - "#eeeeec00", + "#111110ff", + "#191918ff", + "#222221ff", + "#2a2a28ff", + "#31312eff", + "#3b3a37ff", + "#494844ff", + "#62605bff", + "#6f6d66ff", + "#7c7b74ff", + "#b5b3adff", + "#eeeeecff", ], dark_alpha: [ "#00000000", @@ -448,18 +448,18 @@ fn gold() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Gold, light: [ - "#fdfdfc00", - "#faf9f200", - "#f2f0e700", - "#eae6db00", - "#e1dccf00", - "#d8d0bf00", - "#cbc0aa00", - "#b9a88d00", - "#97836500", - "#8c7a5e00", - "#71624b00", - "#3b352b00", + "#fdfdfcff", + "#faf9f2ff", + "#f2f0e7ff", + "#eae6dbff", + "#e1dccfff", + "#d8d0bfff", + "#cbc0aaff", + "#b9a88dff", + "#978365ff", + "#8c7a5eff", + "#71624bff", + "#3b352bff", ], light_alpha: [ "#55550003", @@ -476,18 +476,18 @@ fn gold() -> DefaultColorScaleSet { "#130c00d4", ], dark: [ - "#12121100", - "#1b1a1700", - "#24231f00", - "#2d2b2600", - "#38352e00", - "#44403900", - "#544f4600", - "#69625600", - "#97836500", - "#a3907300", - "#cbb99f00", - "#e8e2d900", + "#121211ff", + "#1b1a17ff", + "#24231fff", + "#2d2b26ff", + "#38352eff", + "#444039ff", + "#544f46ff", + "#696256ff", + "#978365ff", + "#a39073ff", + "#cbb99fff", + "#e8e2d9ff", ], dark_alpha: [ "#91911102", @@ -510,18 +510,18 @@ fn bronze() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Bronze, light: [ - "#fdfcfc00", - "#fdf7f500", - "#f6edea00", - "#efe4df00", - "#e7d9d300", - "#dfcdc500", - "#d3bcb300", - "#c2a49900", - "#a1807200", - "#95746800", - "#7d5e5400", - "#43302b00", + "#fdfcfcff", + "#fdf7f5ff", + "#f6edeaff", + "#efe4dfff", + "#e7d9d3ff", + "#dfcdc5ff", + "#d3bcb3ff", + "#c2a499ff", + "#a18072ff", + "#957468ff", + "#7d5e54ff", + "#43302bff", ], light_alpha: [ "#55000003", @@ -538,18 +538,18 @@ fn bronze() -> DefaultColorScaleSet { "#1d0600d4", ], dark: [ - "#14111000", - "#1c191700", - "#26222000", - "#302a2700", - "#3b333000", - "#493e3a00", - "#5a4c4700", - "#6f5f5800", - "#a1807200", - "#ae8c7e00", - "#d4b3a500", - "#ede0d900", + "#141110ff", + "#1c1917ff", + "#262220ff", + "#302a27ff", + "#3b3330ff", + "#493e3aff", + "#5a4c47ff", + "#6f5f58ff", + "#a18072ff", + "#ae8c7eff", + "#d4b3a5ff", + "#ede0d9ff", ], dark_alpha: [ "#d1110004", @@ -572,18 +572,18 @@ fn brown() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Brown, light: [ - "#fefdfc00", - "#fcf9f600", - "#f6eee700", - "#f0e4d900", - "#ebdaca00", - "#e4cdb700", - "#dcbc9f00", - "#cea37e00", - "#ad7f5800", - "#a0755300", - "#815e4600", - "#3e332e00", + "#fefdfcff", + "#fcf9f6ff", + "#f6eee7ff", + "#f0e4d9ff", + "#ebdacaff", + "#e4cdb7ff", + "#dcbc9fff", + "#cea37eff", + "#ad7f58ff", + "#a07553ff", + "#815e46ff", + "#3e332eff", ], light_alpha: [ "#aa550003", @@ -600,18 +600,18 @@ fn brown() -> DefaultColorScaleSet { "#140600d1", ], dark: [ - "#12110f00", - "#1c181600", - "#28211d00", - "#32292200", - "#3e312800", - "#4d3c2f00", - "#614a3900", - "#7c5f4600", - "#ad7f5800", - "#b88c6700", - "#dbb59400", - "#f2e1ca00", + "#12110fff", + "#1c1816ff", + "#28211dff", + "#322922ff", + "#3e3128ff", + "#4d3c2fff", + "#614a39ff", + "#7c5f46ff", + "#ad7f58ff", + "#b88c67ff", + "#dbb594ff", + "#f2e1caff", ], dark_alpha: [ "#91110002", @@ -634,18 +634,18 @@ fn yellow() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Yellow, light: [ - "#fdfdf900", - "#fefce900", - "#fffab800", - "#fff39400", - "#ffe77000", - "#f3d76800", - "#e4c76700", - "#d5ae3900", - "#ffe62900", - "#ffdc0000", - "#9e6c0000", - "#473b1f00", + "#fdfdf9ff", + "#fefce9ff", + "#fffab8ff", + "#fff394ff", + "#ffe770ff", + "#f3d768ff", + "#e4c767ff", + "#d5ae39ff", + "#ffe629ff", + "#ffdc00ff", + "#9e6c00ff", + "#473b1fff", ], light_alpha: [ "#aaaa0006", @@ -657,23 +657,23 @@ fn yellow() -> DefaultColorScaleSet { "#d2a10098", "#c99700c6", "#ffe100d6", - "#ffdc0000", - "#9e6c0000", + "#ffdc00ff", + "#9e6c00ff", "#2e2000e0", ], dark: [ - "#14120b00", - "#1b180f00", - "#2d230500", - "#362b0000", - "#43350000", - "#52420200", - "#66541700", - "#836a2100", - "#ffe62900", - "#ffff5700", - "#f5e14700", - "#f6eeb400", + "#14120bff", + "#1b180fff", + "#2d2305ff", + "#362b00ff", + "#433500ff", + "#524202ff", + "#665417ff", + "#836a21ff", + "#ffe629ff", + "#ffff57ff", + "#f5e147ff", + "#f6eeb4ff", ], dark_alpha: [ "#d1510004", @@ -684,8 +684,8 @@ fn yellow() -> DefaultColorScaleSet { "#fec40046", "#fdcb225c", "#fdca327b", - "#ffe62900", - "#ffff5700", + "#ffe629ff", + "#ffff57ff", "#fee949f5", "#fef6baf6", ], @@ -696,18 +696,18 @@ fn amber() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Amber, light: [ - "#fefdfb00", - "#fefbe900", - "#fff7c200", - "#ffee9c00", - "#fbe57700", - "#f3d67300", - "#e9c16200", - "#e2a33600", - "#ffc53d00", - "#ffba1800", - "#ab640000", - "#4f342200", + "#fefdfbff", + "#fefbe9ff", + "#fff7c2ff", + "#ffee9cff", + "#fbe577ff", + "#f3d673ff", + "#e9c162ff", + "#e2a336ff", + "#ffc53dff", + "#ffba18ff", + "#ab6400ff", + "#4f3422ff", ], light_alpha: [ "#c0800004", @@ -720,22 +720,22 @@ fn amber() -> DefaultColorScaleSet { "#da8a00c9", "#ffb300c2", "#ffb300e7", - "#ab640000", + "#ab6400ff", "#341500dd", ], dark: [ - "#16120c00", - "#1d180f00", - "#30200800", - "#3f270000", - "#4d300000", - "#5c3d0500", - "#714f1900", - "#8f642400", - "#ffc53d00", - "#ffd60a00", - "#ffca1600", - "#ffe7b300", + "#16120cff", + "#1d180fff", + "#302008ff", + "#3f2700ff", + "#4d3000ff", + "#5c3d05ff", + "#714f19ff", + "#8f6424ff", + "#ffc53dff", + "#ffd60aff", + "#ffca16ff", + "#ffe7b3ff", ], dark_alpha: [ "#e63c0006", @@ -746,10 +746,10 @@ fn amber() -> DefaultColorScaleSet { "#fd9b0051", "#ffab2567", "#ffae3587", - "#ffc53d00", - "#ffd60a00", - "#ffca1600", - "#ffe7b300", + "#ffc53dff", + "#ffd60aff", + "#ffca16ff", + "#ffe7b3ff", ], } } @@ -758,18 +758,18 @@ fn orange() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Orange, light: [ - "#fefcfb00", - "#fff7ed00", - "#ffefd600", - "#ffdfb500", - "#ffd19a00", - "#ffc18200", - "#f5ae7300", - "#ec945500", - "#f76b1500", - "#ef5f0000", - "#cc4e0000", - "#582d1d00", + "#fefcfbff", + "#fff7edff", + "#ffefd6ff", + "#ffdfb5ff", + "#ffd19aff", + "#ffc182ff", + "#f5ae73ff", + "#ec9455ff", + "#f76b15ff", + "#ef5f00ff", + "#cc4e00ff", + "#582d1dff", ], light_alpha: [ "#c0400004", @@ -781,23 +781,23 @@ fn orange() -> DefaultColorScaleSet { "#ed6c008c", "#e35f00aa", "#f65e00ea", - "#ef5f0000", - "#cc4e0000", + "#ef5f00ff", + "#cc4e00ff", "#431200e2", ], dark: [ - "#17120e00", - "#1e160f00", - "#331e0b00", - "#46210000", - "#56280000", - "#66350c00", - "#7e451d00", - "#a3582900", - "#f76b1500", - "#ff801f00", - "#ffa05700", - "#ffe0c200", + "#17120eff", + "#1e160fff", + "#331e0bff", + "#462100ff", + "#562800ff", + "#66350cff", + "#7e451dff", + "#a35829ff", + "#f76b15ff", + "#ff801fff", + "#ffa057ff", + "#ffe0c2ff", ], dark_alpha: [ "#ec360007", @@ -809,9 +809,9 @@ fn orange() -> DefaultColorScaleSet { "#ff832c75", "#fe84389d", "#fe6d15f7", - "#ff801f00", - "#ffa05700", - "#ffe0c200", + "#ff801fff", + "#ffa057ff", + "#ffe0c2ff", ], } } @@ -820,18 +820,18 @@ fn tomato() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Tomato, light: [ - "#fffcfc00", - "#fff8f700", - "#feebe700", - "#ffdcd300", - "#ffcdc200", - "#fdbdaf00", - "#f5a89800", - "#ec8e7b00", - "#e54d2e00", - "#dd442500", - "#d1341500", - "#5c271f00", + "#fffcfcff", + "#fff8f7ff", + "#feebe7ff", + "#ffdcd3ff", + "#ffcdc2ff", + "#fdbdafff", + "#f5a898ff", + "#ec8e7bff", + "#e54d2eff", + "#dd4425ff", + "#d13415ff", + "#5c271fff", ], light_alpha: [ "#ff000003", @@ -848,18 +848,18 @@ fn tomato() -> DefaultColorScaleSet { "#460900e0", ], dark: [ - "#18111100", - "#1f151300", - "#39171400", - "#4e151100", - "#5e1c1600", - "#6e292000", - "#853a2d00", - "#ac4d3900", - "#e54d2e00", - "#ec614200", - "#ff977d00", - "#fbd3cb00", + "#181111ff", + "#1f1513ff", + "#391714ff", + "#4e1511ff", + "#5e1c16ff", + "#6e2920ff", + "#853a2dff", + "#ac4d39ff", + "#e54d2eff", + "#ec6142ff", + "#ff977dff", + "#fbd3cbff", ], dark_alpha: [ "#f1121208", @@ -872,7 +872,7 @@ fn tomato() -> DefaultColorScaleSet { "#fe6d4ea7", "#fe5431e4", "#ff6847eb", - "#ff977d00", + "#ff977dff", "#ffd6cefb", ], } @@ -882,18 +882,18 @@ fn red() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Red, light: [ - "#fffcfc00", - "#fff7f700", - "#feebec00", - "#ffdbdc00", - "#ffcdce00", - "#fdbdbe00", - "#f4a9aa00", - "#eb8e9000", - "#e5484d00", - "#dc3e4200", - "#ce2c3100", - "#64172300", + "#fffcfcff", + "#fff7f7ff", + "#feebecff", + "#ffdbdcff", + "#ffcdceff", + "#fdbdbeff", + "#f4a9aaff", + "#eb8e90ff", + "#e5484dff", + "#dc3e42ff", + "#ce2c31ff", + "#641723ff", ], light_alpha: [ "#ff000003", @@ -910,18 +910,18 @@ fn red() -> DefaultColorScaleSet { "#55000de8", ], dark: [ - "#19111100", - "#20131400", - "#3b121900", - "#500f1c00", - "#61162300", - "#72232d00", - "#8c333a00", - "#b5454800", - "#e5484d00", - "#ec5d5e00", - "#ff959200", - "#ffd1d900", + "#191111ff", + "#201314ff", + "#3b1219ff", + "#500f1cff", + "#611623ff", + "#72232dff", + "#8c333aff", + "#b54548ff", + "#e5484dff", + "#ec5d5eff", + "#ff9592ff", + "#ffd1d9ff", ], dark_alpha: [ "#f4121209", @@ -934,8 +934,8 @@ fn red() -> DefaultColorScaleSet { "#ff5d61b0", "#fe4e54e4", "#ff6465eb", - "#ff959200", - "#ffd1d900", + "#ff9592ff", + "#ffd1d9ff", ], } } @@ -944,18 +944,18 @@ fn ruby() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Ruby, light: [ - "#fffcfd00", - "#fff7f800", - "#feeaed00", - "#ffdce100", - "#ffced600", - "#f8bfc800", - "#efacb800", - "#e592a300", - "#e5466600", - "#dc3b5d00", - "#ca244d00", - "#64172b00", + "#fffcfdff", + "#fff7f8ff", + "#feeaedff", + "#ffdce1ff", + "#ffced6ff", + "#f8bfc8ff", + "#efacb8ff", + "#e592a3ff", + "#e54666ff", + "#dc3b5dff", + "#ca244dff", + "#64172bff", ], light_alpha: [ "#ff005503", @@ -972,18 +972,18 @@ fn ruby() -> DefaultColorScaleSet { "#550016e8", ], dark: [ - "#19111300", - "#1e151700", - "#3a141e00", - "#4e132500", - "#5e1a2e00", - "#6f253900", - "#88344700", - "#b3445a00", - "#e5466600", - "#ec5a7200", - "#ff949d00", - "#fed2e100", + "#191113ff", + "#1e1517ff", + "#3a141eff", + "#4e1325ff", + "#5e1a2eff", + "#6f2539ff", + "#883447ff", + "#b3445aff", + "#e54666ff", + "#ec5a72ff", + "#ff949dff", + "#fed2e1ff", ], dark_alpha: [ "#f4124a09", @@ -996,7 +996,7 @@ fn ruby() -> DefaultColorScaleSet { "#ff5c7cae", "#fe4c70e4", "#ff617beb", - "#ff949d00", + "#ff949dff", "#ffd3e2fe", ], } @@ -1006,18 +1006,18 @@ fn crimson() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Crimson, light: [ - "#fffcfd00", - "#fef7f900", - "#ffe9f000", - "#fedce700", - "#facedd00", - "#f3bed100", - "#eaacc300", - "#e093b200", - "#e93d8200", - "#df347800", - "#cb1d6300", - "#62163900", + "#fffcfdff", + "#fef7f9ff", + "#ffe9f0ff", + "#fedce7ff", + "#faceddff", + "#f3bed1ff", + "#eaacc3ff", + "#e093b2ff", + "#e93d82ff", + "#df3478ff", + "#cb1d63ff", + "#621639ff", ], light_alpha: [ "#ff005503", @@ -1034,18 +1034,18 @@ fn crimson() -> DefaultColorScaleSet { "#530026e9", ], dark: [ - "#19111400", - "#20131800", - "#38152500", - "#4d122f00", - "#5c183900", - "#6d254500", - "#87335600", - "#b0436e00", - "#e93d8200", - "#ee518a00", - "#ff92ad00", - "#fdd3e800", + "#191114ff", + "#201318ff", + "#381525ff", + "#4d122fff", + "#5c1839ff", + "#6d2545ff", + "#873356ff", + "#b0436eff", + "#e93d82ff", + "#ee518aff", + "#ff92adff", + "#fdd3e8ff", ], dark_alpha: [ "#f4126709", @@ -1058,7 +1058,7 @@ fn crimson() -> DefaultColorScaleSet { "#fe5b9bab", "#fe418de8", "#ff5693ed", - "#ff92ad00", + "#ff92adff", "#ffd5eafd", ], } @@ -1068,18 +1068,18 @@ fn pink() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Pink, light: [ - "#fffcfe00", - "#fef7fb00", - "#fee9f500", - "#fbdcef00", - "#f6cee700", - "#efbfdd00", - "#e7acd000", - "#dd93c200", - "#d6409f00", - "#cf389700", - "#c2298a00", - "#65124900", + "#fffcfeff", + "#fef7fbff", + "#fee9f5ff", + "#fbdcefff", + "#f6cee7ff", + "#efbfddff", + "#e7acd0ff", + "#dd93c2ff", + "#d6409fff", + "#cf3897ff", + "#c2298aff", + "#651249ff", ], light_alpha: [ "#ff00aa03", @@ -1096,18 +1096,18 @@ fn pink() -> DefaultColorScaleSet { "#59003bed", ], dark: [ - "#19111700", - "#21121d00", - "#37172f00", - "#4b143d00", - "#591c4700", - "#69295500", - "#83386900", - "#a8488500", - "#d6409f00", - "#de51a800", - "#ff8dcc00", - "#fdd1ea00", + "#191117ff", + "#21121dff", + "#37172fff", + "#4b143dff", + "#591c47ff", + "#692955ff", + "#833869ff", + "#a84885ff", + "#d6409fff", + "#de51a8ff", + "#ff8dccff", + "#fdd1eaff", ], dark_alpha: [ "#f412bc09", @@ -1120,7 +1120,7 @@ fn pink() -> DefaultColorScaleSet { "#ff68c8a2", "#fe49bcd4", "#ff5cc0dc", - "#ff8dcc00", + "#ff8dccff", "#ffd3ecfd", ], } @@ -1130,18 +1130,18 @@ fn plum() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Plum, light: [ - "#fefcff00", - "#fdf7fd00", - "#fbebfb00", - "#f7def800", - "#f2d1f300", - "#e9c2ec00", - "#deade300", - "#cf91d800", - "#ab4aba00", - "#a144af00", - "#953ea300", - "#53195d00", + "#fefcffff", + "#fdf7fdff", + "#fbebfbff", + "#f7def8ff", + "#f2d1f3ff", + "#e9c2ecff", + "#deade3ff", + "#cf91d8ff", + "#ab4abaff", + "#a144afff", + "#953ea3ff", + "#53195dff", ], light_alpha: [ "#aa00ff03", @@ -1158,18 +1158,18 @@ fn plum() -> DefaultColorScaleSet { "#40004be6", ], dark: [ - "#18111800", - "#20132000", - "#351a3500", - "#451d4700", - "#51245400", - "#5e306100", - "#73407900", - "#92549c00", - "#ab4aba00", - "#b658c400", - "#e796f300", - "#f4d4f400", + "#181118ff", + "#201320ff", + "#351a35ff", + "#451d47ff", + "#512454ff", + "#5e3061ff", + "#734079ff", + "#92549cff", + "#ab4abaff", + "#b658c4ff", + "#e796f3ff", + "#f4d4f4ff", ], dark_alpha: [ "#f112f108", @@ -1192,18 +1192,18 @@ fn purple() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Purple, light: [ - "#fefcfe00", - "#fbf7fe00", - "#f7edfe00", - "#f2e2fc00", - "#ead5f900", - "#e0c4f400", - "#d1afec00", - "#be93e400", - "#8e4ec600", - "#8347b900", - "#8145b500", - "#40206000", + "#fefcfeff", + "#fbf7feff", + "#f7edfeff", + "#f2e2fcff", + "#ead5f9ff", + "#e0c4f4ff", + "#d1afecff", + "#be93e4ff", + "#8e4ec6ff", + "#8347b9ff", + "#8145b5ff", + "#402060ff", ], light_alpha: [ "#aa00aa03", @@ -1220,18 +1220,18 @@ fn purple() -> DefaultColorScaleSet { "#250049df", ], dark: [ - "#18111b00", - "#1e152300", - "#301c3b00", - "#3d224e00", - "#48295c00", - "#54346b00", - "#66428200", - "#8457aa00", - "#8e4ec600", - "#9a5cd000", - "#d19dff00", - "#ecd9fa00", + "#18111bff", + "#1e1523ff", + "#301c3bff", + "#3d224eff", + "#48295cff", + "#54346bff", + "#664282ff", + "#8457aaff", + "#8e4ec6ff", + "#9a5cd0ff", + "#d19dffff", + "#ecd9faff", ], dark_alpha: [ "#b412f90b", @@ -1244,7 +1244,7 @@ fn purple() -> DefaultColorScaleSet { "#c47effa4", "#b661ffc2", "#bc6fffcd", - "#d19dff00", + "#d19dffff", "#f1ddfffa", ], } @@ -1254,18 +1254,18 @@ fn violet() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Violet, light: [ - "#fdfcfe00", - "#faf8ff00", - "#f4f0fe00", - "#ebe4ff00", - "#e1d9ff00", - "#d4cafe00", - "#c2b5f500", - "#aa99ec00", - "#6e56cf00", - "#654dc400", - "#6550b900", - "#2f265f00", + "#fdfcfeff", + "#faf8ffff", + "#f4f0feff", + "#ebe4ffff", + "#e1d9ffff", + "#d4cafeff", + "#c2b5f5ff", + "#aa99ecff", + "#6e56cfff", + "#654dc4ff", + "#6550b9ff", + "#2f265fff", ], light_alpha: [ "#5500aa03", @@ -1282,18 +1282,18 @@ fn violet() -> DefaultColorScaleSet { "#0b0043d9", ], dark: [ - "#14121f00", - "#1b152500", - "#291f4300", - "#33255b00", - "#3c2e6900", - "#47387600", - "#56468b00", - "#6958ad00", - "#6e56cf00", - "#7d66d900", - "#baa7ff00", - "#e2ddfe00", + "#14121fff", + "#1b1525ff", + "#291f43ff", + "#33255bff", + "#3c2e69ff", + "#473876ff", + "#56468bff", + "#6958adff", + "#6e56cfff", + "#7d66d9ff", + "#baa7ffff", + "#e2ddfeff", ], dark_alpha: [ "#4422ff0f", @@ -1306,7 +1306,7 @@ fn violet() -> DefaultColorScaleSet { "#977dfea8", "#8668ffcc", "#9176fed7", - "#baa7ff00", + "#baa7ffff", "#e3defffe", ], } @@ -1316,18 +1316,18 @@ fn iris() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Iris, light: [ - "#fdfdff00", - "#f8f8ff00", - "#f0f1fe00", - "#e6e7ff00", - "#dadcff00", - "#cbcdff00", - "#b8baf800", - "#9b9ef000", - "#5b5bd600", - "#5151cd00", - "#5753c600", - "#27296200", + "#fdfdffff", + "#f8f8ffff", + "#f0f1feff", + "#e6e7ffff", + "#dadcffff", + "#cbcdffff", + "#b8baf8ff", + "#9b9ef0ff", + "#5b5bd6ff", + "#5151cdff", + "#5753c6ff", + "#272962ff", ], light_alpha: [ "#0000ff02", @@ -1344,18 +1344,18 @@ fn iris() -> DefaultColorScaleSet { "#000246d8", ], dark: [ - "#13131e00", - "#17162500", - "#20224800", - "#262a6500", - "#30337400", - "#3d3e8200", - "#4a4a9500", - "#5958b100", - "#5b5bd600", - "#6e6ade00", - "#b1a9ff00", - "#e0dffe00", + "#13131eff", + "#171625ff", + "#202248ff", + "#262a65ff", + "#303374ff", + "#3d3e82ff", + "#4a4a95ff", + "#5958b1ff", + "#5b5bd6ff", + "#6e6adeff", + "#b1a9ffff", + "#e0dffeff", ], dark_alpha: [ "#3636fe0e", @@ -1368,7 +1368,7 @@ fn iris() -> DefaultColorScaleSet { "#7b7afeac", "#6a6afed4", "#7d79ffdc", - "#b1a9ff00", + "#b1a9ffff", "#e1e0fffe", ], } @@ -1378,18 +1378,18 @@ fn indigo() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Indigo, light: [ - "#fdfdfe00", - "#f7f9ff00", - "#edf2fe00", - "#e1e9ff00", - "#d2deff00", - "#c1d0ff00", - "#abbdf900", - "#8da4ef00", - "#3e63dd00", - "#3358d400", - "#3a5bc700", - "#1f2d5c00", + "#fdfdfeff", + "#f7f9ffff", + "#edf2feff", + "#e1e9ffff", + "#d2deffff", + "#c1d0ffff", + "#abbdf9ff", + "#8da4efff", + "#3e63ddff", + "#3358d4ff", + "#3a5bc7ff", + "#1f2d5cff", ], light_alpha: [ "#00008002", @@ -1406,18 +1406,18 @@ fn indigo() -> DefaultColorScaleSet { "#001046e0", ], dark: [ - "#11131f00", - "#14172600", - "#18244900", - "#1d2e6200", - "#25397400", - "#30438400", - "#3a4f9700", - "#435db100", - "#3e63dd00", - "#5472e400", - "#9eb1ff00", - "#d6e1ff00", + "#11131fff", + "#141726ff", + "#182449ff", + "#1d2e62ff", + "#253974ff", + "#304384ff", + "#3a4f97ff", + "#435db1ff", + "#3e63ddff", + "#5472e4ff", + "#9eb1ffff", + "#d6e1ffff", ], dark_alpha: [ "#1133ff0f", @@ -1430,8 +1430,8 @@ fn indigo() -> DefaultColorScaleSet { "#5b81feac", "#4671ffdb", "#5c7efee3", - "#9eb1ff00", - "#d6e1ff00", + "#9eb1ffff", + "#d6e1ffff", ], } } @@ -1440,18 +1440,18 @@ fn blue() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Blue, light: [ - "#fbfdff00", - "#f4faff00", - "#e6f4fe00", - "#d5efff00", - "#c2e5ff00", - "#acd8fc00", - "#8ec8f600", - "#5eb1ef00", - "#0090ff00", - "#0588f000", - "#0d74ce00", - "#11326400", + "#fbfdffff", + "#f4faffff", + "#e6f4feff", + "#d5efffff", + "#c2e5ffff", + "#acd8fcff", + "#8ec8f6ff", + "#5eb1efff", + "#0090ffff", + "#0588f0ff", + "#0d74ceff", + "#113264ff", ], light_alpha: [ "#0080ff04", @@ -1462,24 +1462,24 @@ fn blue() -> DefaultColorScaleSet { "#0088f653", "#0083eb71", "#0084e6a1", - "#0090ff00", + "#0090ffff", "#0086f0fa", "#006dcbf2", "#002359ee", ], dark: [ - "#0d152000", - "#11192700", - "#0d284700", - "#00336200", - "#00407400", - "#104d8700", - "#205d9e00", - "#2870bd00", - "#0090ff00", - "#3b9eff00", - "#70b8ff00", - "#c2e6ff00", + "#0d1520ff", + "#111927ff", + "#0d2847ff", + "#003362ff", + "#004074ff", + "#104d87ff", + "#205d9eff", + "#2870bdff", + "#0090ffff", + "#3b9effff", + "#70b8ffff", + "#c2e6ffff", ], dark_alpha: [ "#004df211", @@ -1490,10 +1490,10 @@ fn blue() -> DefaultColorScaleSet { "#0f89fd7f", "#2a91fe98", "#3094feb9", - "#0090ff00", - "#3b9eff00", - "#70b8ff00", - "#c2e6ff00", + "#0090ffff", + "#3b9effff", + "#70b8ffff", + "#c2e6ffff", ], } } @@ -1502,18 +1502,18 @@ fn cyan() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Cyan, light: [ - "#fafdfe00", - "#f2fafb00", - "#def7f900", - "#caf1f600", - "#b5e9f000", - "#9ddde700", - "#7dcedc00", - "#3db9cf00", - "#00a2c700", - "#0797b900", - "#107d9800", - "#0d3c4800", + "#fafdfeff", + "#f2fafbff", + "#def7f9ff", + "#caf1f6ff", + "#b5e9f0ff", + "#9ddde7ff", + "#7dcedcff", + "#3db9cfff", + "#00a2c7ff", + "#0797b9ff", + "#107d98ff", + "#0d3c48ff", ], light_alpha: [ "#0099cc05", @@ -1524,24 +1524,24 @@ fn cyan() -> DefaultColorScaleSet { "#00a7c162", "#009fbb82", "#00a3c0c2", - "#00a2c700", + "#00a2c7ff", "#0094b7f8", "#007491ef", "#00323ef2", ], dark: [ - "#0b161a00", - "#101b2000", - "#082c3600", - "#00384800", - "#00455800", - "#04546800", - "#12677e00", - "#11809c00", - "#00a2c700", - "#23afd000", - "#4ccce600", - "#b6ecf700", + "#0b161aff", + "#101b20ff", + "#082c36ff", + "#003848ff", + "#004558ff", + "#045468ff", + "#12677eff", + "#11809cff", + "#00a2c7ff", + "#23afd0ff", + "#4ccce6ff", + "#b6ecf7ff", ], dark_alpha: [ "#0091f70a", @@ -1564,18 +1564,18 @@ fn teal() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Teal, light: [ - "#fafefd00", - "#f3fbf900", - "#e0f8f300", - "#ccf3ea00", - "#b8eae000", - "#a1ded200", - "#83cdc100", - "#53b9ab00", - "#12a59400", - "#0d9b8a00", - "#00857300", - "#0d3d3800", + "#fafefdff", + "#f3fbf9ff", + "#e0f8f3ff", + "#ccf3eaff", + "#b8eae0ff", + "#a1ded2ff", + "#83cdc1ff", + "#53b9abff", + "#12a594ff", + "#0d9b8aff", + "#008573ff", + "#0d3d38ff", ], light_alpha: [ "#00cc9905", @@ -1588,22 +1588,22 @@ fn teal() -> DefaultColorScaleSet { "#009783ac", "#009e8ced", "#009684f2", - "#00857300", + "#008573ff", "#00332df2", ], dark: [ - "#0d151400", - "#111c1b00", - "#0d2d2a00", - "#023b3700", - "#08484300", - "#14575000", - "#1c696100", - "#207e7300", - "#12a59400", - "#0eb39e00", - "#0bd8b600", - "#adf0dd00", + "#0d1514ff", + "#111c1bff", + "#0d2d2aff", + "#023b37ff", + "#084843ff", + "#145750ff", + "#1c6961ff", + "#207e73ff", + "#12a594ff", + "#0eb39eff", + "#0bd8b6ff", + "#adf0ddff", ], dark_alpha: [ "#00deab05", @@ -1626,18 +1626,18 @@ fn jade() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Jade, light: [ - "#fbfefd00", - "#f4fbf700", - "#e6f7ed00", - "#d6f1e300", - "#c3e9d700", - "#acdec800", - "#8bceb600", - "#56ba9f00", - "#29a38300", - "#26997b00", - "#20836800", - "#1d3b3100", + "#fbfefdff", + "#f4fbf7ff", + "#e6f7edff", + "#d6f1e3ff", + "#c3e9d7ff", + "#acdec8ff", + "#8bceb6ff", + "#56ba9fff", + "#29a383ff", + "#26997bff", + "#208368ff", + "#1d3b31ff", ], light_alpha: [ "#00c08004", @@ -1654,18 +1654,18 @@ fn jade() -> DefaultColorScaleSet { "#002217e2", ], dark: [ - "#0d151200", - "#121c1800", - "#0f2e2200", - "#0b3b2c00", - "#11483700", - "#1b574500", - "#24685400", - "#2a7e6800", - "#29a38300", - "#27b08b00", - "#1fd8a400", - "#adf0d400", + "#0d1512ff", + "#121c18ff", + "#0f2e22ff", + "#0b3b2cff", + "#114837ff", + "#1b5745ff", + "#246854ff", + "#2a7e68ff", + "#29a383ff", + "#27b08bff", + "#1fd8a4ff", + "#adf0d4ff", ], dark_alpha: [ "#00de4505", @@ -1688,18 +1688,18 @@ fn green() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Green, light: [ - "#fbfefc00", - "#f4fbf600", - "#e6f6eb00", - "#d6f1df00", - "#c4e8d100", - "#adddc000", - "#8eceaa00", - "#5bb98b00", - "#30a46c00", - "#2b9a6600", - "#21835800", - "#193b2d00", + "#fbfefcff", + "#f4fbf6ff", + "#e6f6ebff", + "#d6f1dfff", + "#c4e8d1ff", + "#adddc0ff", + "#8eceaaff", + "#5bb98bff", + "#30a46cff", + "#2b9a66ff", + "#218358ff", + "#193b2dff", ], light_alpha: [ "#00c04004", @@ -1716,18 +1716,18 @@ fn green() -> DefaultColorScaleSet { "#002616e6", ], dark: [ - "#0e151200", - "#121b1700", - "#132d2100", - "#113b2900", - "#17493300", - "#20573e00", - "#28684a00", - "#2f7c5700", - "#30a46c00", - "#33b07400", - "#3dd68c00", - "#b1f1cb00", + "#0e1512ff", + "#121b17ff", + "#132d21ff", + "#113b29ff", + "#174933ff", + "#20573eff", + "#28684aff", + "#2f7c57ff", + "#30a46cff", + "#33b074ff", + "#3dd68cff", + "#b1f1cbff", ], dark_alpha: [ "#00de4505", @@ -1750,18 +1750,18 @@ fn grass() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Grass, light: [ - "#fbfefb00", - "#f5fbf500", - "#e9f6e900", - "#daf1db00", - "#c9e8ca00", - "#b2ddb500", - "#94ce9a00", - "#65ba7400", - "#46a75800", - "#3e9b4f00", - "#2a7e3b00", - "#203c2500", + "#fbfefbff", + "#f5fbf5ff", + "#e9f6e9ff", + "#daf1dbff", + "#c9e8caff", + "#b2ddb5ff", + "#94ce9aff", + "#65ba74ff", + "#46a758ff", + "#3e9b4fff", + "#2a7e3bff", + "#203c25ff", ], light_alpha: [ "#00c00004", @@ -1778,18 +1778,18 @@ fn grass() -> DefaultColorScaleSet { "#002006df", ], dark: [ - "#0e151100", - "#141a1500", - "#1b2a1e00", - "#1d3a2400", - "#25482d00", - "#2d573600", - "#36674000", - "#3e794900", - "#46a75800", - "#53b36500", - "#71d08300", - "#c2f0c200", + "#0e1511ff", + "#141a15ff", + "#1b2a1eff", + "#1d3a24ff", + "#25482dff", + "#2d5736ff", + "#366740ff", + "#3e7949ff", + "#46a758ff", + "#53b365ff", + "#71d083ff", + "#c2f0c2ff", ], dark_alpha: [ "#00de1205", @@ -1812,18 +1812,18 @@ fn lime() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Lime, light: [ - "#fcfdfa00", - "#f8faf300", - "#eef6d600", - "#e2f0bd00", - "#d3e7a600", - "#c2da9100", - "#abc97800", - "#8db65400", - "#bdee6300", - "#b0e64c00", - "#5c7c2f00", - "#37401c00", + "#fcfdfaff", + "#f8faf3ff", + "#eef6d6ff", + "#e2f0bdff", + "#d3e7a6ff", + "#c2da91ff", + "#abc978ff", + "#8db654ff", + "#bdee63ff", + "#b0e64cff", + "#5c7c2fff", + "#37401cff", ], light_alpha: [ "#66990005", @@ -1840,18 +1840,18 @@ fn lime() -> DefaultColorScaleSet { "#1e2900e3", ], dark: [ - "#11130c00", - "#151a1000", - "#1f291700", - "#29371d00", - "#33442300", - "#3d522a00", - "#49623100", - "#57753800", - "#bdee6300", - "#d4ff7000", - "#bde56c00", - "#e3f7ba00", + "#11130cff", + "#151a10ff", + "#1f2917ff", + "#29371dff", + "#334423ff", + "#3d522aff", + "#496231ff", + "#577538ff", + "#bdee63ff", + "#d4ff70ff", + "#bde56cff", + "#e3f7baff", ], dark_alpha: [ "#11bb0003", @@ -1863,7 +1863,7 @@ fn lime() -> DefaultColorScaleSet { "#b6ff6f57", "#b6fd6d6c", "#caff69ed", - "#d4ff7000", + "#d4ff70ff", "#d1fe77e4", "#e9febff7", ], @@ -1874,18 +1874,18 @@ fn mint() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Mint, light: [ - "#f9fefd00", - "#f2fbf900", - "#ddf9f200", - "#c8f4e900", - "#b3ecde00", - "#9ce0d000", - "#7ecfbd00", - "#4cbba500", - "#86ead400", - "#7de0cb00", - "#02786400", - "#16433c00", + "#f9fefdff", + "#f2fbf9ff", + "#ddf9f2ff", + "#c8f4e9ff", + "#b3ecdeff", + "#9ce0d0ff", + "#7ecfbdff", + "#4cbba5ff", + "#86ead4ff", + "#7de0cbff", + "#027864ff", + "#16433cff", ], light_alpha: [ "#00d5aa06", @@ -1902,18 +1902,18 @@ fn mint() -> DefaultColorScaleSet { "#00312ae9", ], dark: [ - "#0e151500", - "#0f1b1b00", - "#092c2b00", - "#003a3800", - "#00474400", - "#10565000", - "#1e685f00", - "#277f7000", - "#86ead400", - "#a8f5e500", - "#58d5ba00", - "#c4f5e100", + "#0e1515ff", + "#0f1b1bff", + "#092c2bff", + "#003a38ff", + "#004744ff", + "#105650ff", + "#1e685fff", + "#277f70ff", + "#86ead4ff", + "#a8f5e5ff", + "#58d5baff", + "#c4f5e1ff", ], dark_alpha: [ "#00dede05", @@ -1936,18 +1936,18 @@ fn sky() -> DefaultColorScaleSet { DefaultColorScaleSet { scale: ColorScaleName::Sky, light: [ - "#f9feff00", - "#f1fafd00", - "#e1f6fd00", - "#d1f0fa00", - "#bee7f500", - "#a9daed00", - "#8dcae300", - "#60b3d700", - "#7ce2fe00", - "#74daf800", - "#00749e00", - "#1d3e5600", + "#f9feffff", + "#f1fafdff", + "#e1f6fdff", + "#d1f0faff", + "#bee7f5ff", + "#a9daedff", + "#8dcae3ff", + "#60b3d7ff", + "#7ce2feff", + "#74daf8ff", + "#00749eff", + "#1d3e56ff", ], light_alpha: [ "#00d5ff06", @@ -1960,22 +1960,22 @@ fn sky() -> DefaultColorScaleSet { "#0085bf9f", "#00c7fe83", "#00bcf38b", - "#00749e00", + "#00749eff", "#002540e2", ], dark: [ - "#0d141f00", - "#111a2700", - "#11284000", - "#11355500", - "#15446700", - "#1b537b00", - "#1f669200", - "#197cae00", - "#7ce2fe00", - "#a8eeff00", - "#75c7f000", - "#c2f3ff00", + "#0d141fff", + "#111a27ff", + "#112840ff", + "#113555ff", + "#154467ff", + "#1b537bff", + "#1f6692ff", + "#197caeff", + "#7ce2feff", + "#a8eeffff", + "#75c7f0ff", + "#c2f3ffff", ], dark_alpha: [ "#0044ff0f", @@ -1987,9 +1987,9 @@ fn sky() -> DefaultColorScaleSet { "#2badfe8b", "#1db2fea9", "#7ce3fffe", - "#a8eeff00", + "#a8eeffff", "#7cd3ffef", - "#c2f3ff00", + "#c2f3ffff", ], } } From a02d80671596b4cfa8afea1a8e7b5294faf46318 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:31:15 -0400 Subject: [PATCH 48/54] Add a story showcasing the color scales --- crates/storybook2/src/stories.rs | 2 ++ crates/storybook2/src/stories/colors.rs | 28 +++++++++++++++++++++++++ crates/storybook2/src/story_selector.rs | 2 ++ crates/theme2/src/default.rs | 5 ++--- crates/theme2/src/scale.rs | 9 ++++---- crates/theme2/src/theme2.rs | 2 ++ 6 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 crates/storybook2/src/stories/colors.rs diff --git a/crates/storybook2/src/stories.rs b/crates/storybook2/src/stories.rs index 2517522bc3..3d8a332fb9 100644 --- a/crates/storybook2/src/stories.rs +++ b/crates/storybook2/src/stories.rs @@ -1,9 +1,11 @@ +mod colors; mod focus; mod kitchen_sink; mod scroll; mod text; mod z_index; +pub use colors::*; pub use focus::*; pub use kitchen_sink::*; pub use scroll::*; diff --git a/crates/storybook2/src/stories/colors.rs b/crates/storybook2/src/stories/colors.rs new file mode 100644 index 0000000000..a0a0620085 --- /dev/null +++ b/crates/storybook2/src/stories/colors.rs @@ -0,0 +1,28 @@ +use ui::prelude::*; + +use crate::story::Story; + +#[derive(Component)] +pub struct ColorsStory; + +impl ColorsStory { + fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + let color_scales = theme2::default_color_scales(); + + Story::container(cx) + .child(Story::title(cx, "Colors")) + .child( + div() + .id("colors") + .flex() + .flex_col() + .overflow_y_scroll() + .text_color(gpui2::white()) + .children(color_scales.into_iter().map(|(name, scale)| { + div().child(name.to_string()).child(div().flex().children( + (1..=12).map(|step| div().flex().size_4().bg(scale.step(cx, step))), + )) + })), + ) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index d6ff77c5c1..737f28bda2 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -14,6 +14,7 @@ use ui::prelude::*; pub enum ElementStory { Avatar, Button, + Colors, Details, Focus, Icon, @@ -29,6 +30,7 @@ impl ElementStory { match self { Self::Avatar => { cx.build_view(|cx| (), |_, _| ui::AvatarStory.render()) }.into_any(), Self::Button => { cx.build_view(|cx| (), |_, _| ui::ButtonStory.render()) }.into_any(), + Self::Colors => { cx.build_view(|cx| (), |_, _| ColorsStory.render()) }.into_any(), Self::Details => { { cx.build_view(|cx| (), |_, _| ui::DetailsStory.render()) }.into_any() } diff --git a/crates/theme2/src/default.rs b/crates/theme2/src/default.rs index e3a0527f11..41d408f980 100644 --- a/crates/theme2/src/default.rs +++ b/crates/theme2/src/default.rs @@ -1,6 +1,5 @@ -use std::collections::HashMap; - use gpui2::Rgba; +use indexmap::IndexMap; use crate::scale::{ColorScaleName, ColorScaleSet, ColorScales}; @@ -35,7 +34,7 @@ impl From for ColorScaleSet { pub fn default_color_scales() -> ColorScales { use ColorScaleName::*; - HashMap::from_iter([ + IndexMap::from_iter([ (Gray, gray().into()), (Mauve, mauve().into()), (Slate, slate().into()), diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs index 3aea476228..ba37924375 100644 --- a/crates/theme2/src/scale.rs +++ b/crates/theme2/src/scale.rs @@ -1,6 +1,5 @@ -use std::collections::HashMap; - use gpui2::{AppContext, Hsla}; +use indexmap::IndexMap; use crate::{theme, Appearance}; @@ -87,7 +86,7 @@ impl std::fmt::Display for ColorScaleName { pub type ColorScale = [Hsla; 12]; -pub type ColorScales = HashMap; +pub type ColorScales = IndexMap; /// A one-based step in a [`ColorScale`]. pub type ColorScaleStep = usize; @@ -146,7 +145,7 @@ impl ColorScaleSet { } } - pub fn step(self, cx: &AppContext, step: ColorScaleStep) -> Hsla { + pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla { let appearance = Self::current_appearance(cx); match appearance { @@ -155,7 +154,7 @@ impl ColorScaleSet { } } - pub fn step_alpha(self, cx: &AppContext, step: ColorScaleStep) -> Hsla { + pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla { let appearance = Self::current_appearance(cx); match appearance { Appearance::Light => self.light_alpha(step), diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 66d70296d2..9425593070 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -4,7 +4,9 @@ mod scale; mod settings; mod themes; +pub use default::*; pub use registry::*; +pub use scale::*; pub use settings::*; use gpui2::{AppContext, HighlightStyle, Hsla, SharedString}; From 49571127dac3829a73f78f19e83b7fab018c3f95 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:35:13 -0400 Subject: [PATCH 49/54] theme2: Correctly reference the dark alpha scale --- crates/theme2/src/scale.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs index ba37924375..22a607bf07 100644 --- a/crates/theme2/src/scale.rs +++ b/crates/theme2/src/scale.rs @@ -133,7 +133,7 @@ impl ColorScaleSet { } pub fn dark_alpha(&self, step: ColorScaleStep) -> Hsla { - self.dark[step - 1] + self.dark_alpha[step - 1] } fn current_appearance(cx: &AppContext) -> Appearance { From 613afd3f6657acc84dc31cce3a69a1d78b8a3ee1 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 30 Oct 2023 17:41:26 -0400 Subject: [PATCH 50/54] Port PR #3131 to zed2 --- crates/client2/src/telemetry.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/client2/src/telemetry.rs b/crates/client2/src/telemetry.rs index 1b64e94107..47d1c143e1 100644 --- a/crates/client2/src/telemetry.rs +++ b/crates/client2/src/telemetry.rs @@ -5,7 +5,9 @@ use parking_lot::Mutex; use serde::Serialize; use settings2::Settings; use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration}; -use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt}; +use sysinfo::{ + CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt, +}; use tempfile::NamedTempFile; use util::http::HttpClient; use util::{channel::ReleaseChannel, TryFutureExt}; @@ -161,8 +163,16 @@ impl Telemetry { let this = self.clone(); cx.spawn(|cx| async move { - let mut system = System::new_all(); - system.refresh_all(); + // Avoiding calling `System::new_all()`, as there have been crashes related to it + let refresh_kind = RefreshKind::new() + .with_memory() // For memory usage + .with_processes(ProcessRefreshKind::everything()) // For process usage + .with_cpu(CpuRefreshKind::everything()); // For core count + + let mut system = System::new_with_specifics(refresh_kind); + + // Avoiding calling `refresh_all()`, just update what we need + system.refresh_specifics(refresh_kind); loop { // Waiting some amount of time before the first query is important to get a reasonable value @@ -170,8 +180,7 @@ impl Telemetry { const DURATION_BETWEEN_SYSTEM_EVENTS: Duration = Duration::from_secs(60); smol::Timer::after(DURATION_BETWEEN_SYSTEM_EVENTS).await; - system.refresh_memory(); - system.refresh_processes(); + system.refresh_specifics(refresh_kind); let current_process = Pid::from_u32(std::process::id()); let Some(process) = system.processes().get(¤t_process) else { From 607813e646e5e140fe08821c73ac3a25cd8c756e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 30 Oct 2023 17:45:37 -0400 Subject: [PATCH 51/54] Tweak style for color scale story --- crates/storybook2/src/stories/colors.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/storybook2/src/stories/colors.rs b/crates/storybook2/src/stories/colors.rs index a0a0620085..d0f938e57d 100644 --- a/crates/storybook2/src/stories/colors.rs +++ b/crates/storybook2/src/stories/colors.rs @@ -1,3 +1,4 @@ +use gpui2::px; use ui::prelude::*; use crate::story::Story; @@ -16,12 +17,21 @@ impl ColorsStory { .id("colors") .flex() .flex_col() + .gap_1() .overflow_y_scroll() .text_color(gpui2::white()) .children(color_scales.into_iter().map(|(name, scale)| { - div().child(name.to_string()).child(div().flex().children( - (1..=12).map(|step| div().flex().size_4().bg(scale.step(cx, step))), - )) + div() + .flex() + .child( + div() + .w(px(75.)) + .line_height(px(24.)) + .child(name.to_string()), + ) + .child(div().flex().gap_1().children( + (1..=12).map(|step| div().flex().size_6().bg(scale.step(cx, step))), + )) })), ) } From d219ddbdaf8b8cd6112eb2091f2977cae3d10f42 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 30 Oct 2023 18:13:18 -0400 Subject: [PATCH 52/54] Add script to get crate-level completion status --- script/zed-2-progress-report.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 script/zed-2-progress-report.py diff --git a/script/zed-2-progress-report.py b/script/zed-2-progress-report.py new file mode 100644 index 0000000000..c4ed3e65d0 --- /dev/null +++ b/script/zed-2-progress-report.py @@ -0,0 +1,28 @@ +import os +from pathlib import Path + +THIS_SCRIPT_PATH: Path = Path(__file__) +CRATES_DIR: Path = THIS_SCRIPT_PATH.parent.parent / "crates" +print(CRATES_DIR) + +zed_1_crate_count: int = 0 +zed_2_crate_count: int = 0 + +for child in os.listdir(CRATES_DIR): + child_path: str = os.path.join(CRATES_DIR, child) + + if not os.path.isdir(child_path): + continue + + if child.endswith("2"): + zed_2_crate_count += 1 + else: + zed_1_crate_count += 1 + +print(f"crates ported: {zed_2_crate_count}") +print(f"crates in total: {zed_1_crate_count}") + +percent_complete: float = (zed_2_crate_count / zed_1_crate_count) * 100 +percent_complete_rounded: float = round(percent_complete, 2) + +print(f"progress: {percent_complete_rounded}%") From f33fc1b6fa4edd45697fe50fbb3624e785e86857 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 30 Oct 2023 18:14:04 -0400 Subject: [PATCH 53/54] Remove print statement --- script/zed-2-progress-report.py | 1 - 1 file changed, 1 deletion(-) diff --git a/script/zed-2-progress-report.py b/script/zed-2-progress-report.py index c4ed3e65d0..87f7f7b8f7 100644 --- a/script/zed-2-progress-report.py +++ b/script/zed-2-progress-report.py @@ -3,7 +3,6 @@ from pathlib import Path THIS_SCRIPT_PATH: Path = Path(__file__) CRATES_DIR: Path = THIS_SCRIPT_PATH.parent.parent / "crates" -print(CRATES_DIR) zed_1_crate_count: int = 0 zed_2_crate_count: int = 0 From 30dffbb40902668d5687f2599a203801c9ee9901 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 30 Oct 2023 15:19:40 -0700 Subject: [PATCH 54/54] Introduce a Render trait, make views implement it Don't pass a render function separately from the view. Co-authored-by: Nathan Sobo Co-authored-by: Mikayla Co-authored-by: Antonio --- crates/gpui2/src/app.rs | 5 +- crates/gpui2/src/app/entity_map.rs | 11 +- crates/gpui2/src/gpui2.rs | 33 ++-- crates/gpui2/src/interactive.rs | 72 ++++----- crates/gpui2/src/view.rs | 41 ++++- crates/gpui2/src/window.rs | 56 +++---- crates/storybook2/src/stories/focus.rs | 149 +++++++++--------- crates/storybook2/src/stories/kitchen_sink.rs | 27 ++-- crates/storybook2/src/stories/scroll.rs | 86 +++++----- crates/storybook2/src/stories/text.rs | 31 ++-- crates/storybook2/src/stories/z_index.rs | 9 +- crates/storybook2/src/story_selector.rs | 104 ++++-------- crates/storybook2/src/storybook2.rs | 17 +- crates/ui2/src/components/assistant_panel.rs | 15 +- crates/ui2/src/components/breadcrumb.rs | 18 +-- crates/ui2/src/components/buffer.rs | 13 +- crates/ui2/src/components/buffer_search.rs | 16 +- crates/ui2/src/components/chat_panel.rs | 8 +- crates/ui2/src/components/collab_panel.rs | 11 +- crates/ui2/src/components/command_palette.rs | 9 +- crates/ui2/src/components/context_menu.rs | 11 +- crates/ui2/src/components/copilot.rs | 9 +- crates/ui2/src/components/editor_pane.rs | 18 +-- crates/ui2/src/components/facepile.rs | 11 +- crates/ui2/src/components/keybinding.rs | 14 +- .../ui2/src/components/language_selector.rs | 11 +- crates/ui2/src/components/multi_buffer.rs | 11 +- .../ui2/src/components/notifications_panel.rs | 11 +- crates/ui2/src/components/palette.rs | 101 ++++++------ crates/ui2/src/components/panel.rs | 13 +- crates/ui2/src/components/panes.rs | 13 +- crates/ui2/src/components/project_panel.rs | 11 +- crates/ui2/src/components/recent_projects.rs | 11 +- crates/ui2/src/components/tab.rs | 31 ++-- crates/ui2/src/components/tab_bar.rs | 11 +- crates/ui2/src/components/terminal.rs | 12 +- crates/ui2/src/components/theme_selector.rs | 9 +- crates/ui2/src/components/title_bar.rs | 39 +++-- crates/ui2/src/components/toast.rs | 11 +- crates/ui2/src/components/toolbar.rs | 11 +- crates/ui2/src/components/traffic_lights.rs | 9 +- crates/ui2/src/components/workspace.rs | 38 +++-- crates/ui2/src/elements/avatar.rs | 11 +- crates/ui2/src/elements/button.rs | 17 +- crates/ui2/src/elements/details.rs | 13 +- crates/ui2/src/elements/icon.rs | 10 +- crates/ui2/src/elements/input.rs | 11 +- crates/ui2/src/elements/label.rs | 11 +- crates/ui2/src/static_data.rs | 8 +- 49 files changed, 616 insertions(+), 612 deletions(-) diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 0523609db3..1d2c17d357 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -898,11 +898,8 @@ impl DerefMut for GlobalLease { /// Contains state associated with an active drag operation, started by dragging an element /// within the window or by dragging into the app from the underlying platform. pub(crate) struct AnyDrag { - pub render: Box AnyElement<()>>, - pub drag_handle_view: Option, + pub view: AnyView, pub cursor_offset: Point, - pub state: AnyBox, - pub state_type: TypeId, } #[cfg(test)] diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index bfd457e4ff..7e0c5626a5 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/crates/gpui2/src/app/entity_map.rs @@ -147,7 +147,7 @@ pub struct Slot(Model); pub struct AnyModel { pub(crate) entity_id: EntityId, - entity_type: TypeId, + pub(crate) entity_type: TypeId, entity_map: Weak>, } @@ -247,8 +247,8 @@ impl Eq for AnyModel {} pub struct Model { #[deref] #[deref_mut] - any_model: AnyModel, - entity_type: PhantomData, + pub(crate) any_model: AnyModel, + pub(crate) entity_type: PhantomData, } unsafe impl Send for Model {} @@ -272,6 +272,11 @@ impl Model { } } + /// Convert this into a dynamically typed model. + pub fn into_any(self) -> AnyModel { + self.any_model + } + pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T { cx.entities.read(self) } diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index d85d1e0c74..85300c1a4a 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -90,13 +90,11 @@ pub trait Context { pub trait VisualContext: Context { type ViewContext<'a, 'w, V>; - fn build_view( + fn build_view( &mut self, - build_model: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, - render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, + build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, ) -> Self::Result> where - E: Component, V: 'static + Send; fn update_view( @@ -171,27 +169,22 @@ impl Context for MainThread { impl VisualContext for MainThread { type ViewContext<'a, 'w, V> = MainThread>; - fn build_view( + fn build_view( &mut self, - build_model: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, - render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, + build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, ) -> Self::Result> where - E: Component, V: 'static + Send, { - self.0.build_view( - |cx| { - let cx = unsafe { - mem::transmute::< - &mut C::ViewContext<'_, '_, V>, - &mut MainThread>, - >(cx) - }; - build_model(cx) - }, - render, - ) + self.0.build_view(|cx| { + let cx = unsafe { + mem::transmute::< + &mut C::ViewContext<'_, '_, V>, + &mut MainThread>, + >(cx) + }; + build_view_state(cx) + }) } fn update_view( diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 43fd83a55f..317d7cea61 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -1,7 +1,7 @@ use crate::{ - point, px, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, Component, - DispatchContext, DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, - Modifiers, Overflow, Pixels, Point, SharedString, Size, Style, StyleRefinement, View, + div, point, px, Action, AnyDrag, AnyView, AppContext, BorrowWindow, Bounds, Component, + DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Keystroke, + Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, View, ViewContext, }; use collections::HashMap; @@ -258,17 +258,17 @@ pub trait StatelessInteractive: Element { self } - fn on_drop( + fn on_drop( mut self, - listener: impl Fn(&mut V, S, &mut ViewContext) + Send + 'static, + listener: impl Fn(&mut V, View, &mut ViewContext) + Send + 'static, ) -> Self where Self: Sized, { self.stateless_interaction().drop_listeners.push(( - TypeId::of::(), - Box::new(move |view, drag_state, cx| { - listener(view, *drag_state.downcast().unwrap(), cx); + TypeId::of::(), + Box::new(move |view, dragged_view, cx| { + listener(view, dragged_view.downcast().unwrap(), cx); }), )); self @@ -314,43 +314,22 @@ pub trait StatefulInteractive: StatelessInteractive { self } - fn on_drag( + fn on_drag( mut self, - listener: impl Fn(&mut V, &mut ViewContext) -> Drag + Send + 'static, + listener: impl Fn(&mut V, &mut ViewContext) -> View + Send + 'static, ) -> Self where Self: Sized, - S: Any + Send, - R: Fn(&mut V, &mut ViewContext) -> E, - R: 'static + Send, - E: Component, + W: 'static + Send + Render, { debug_assert!( self.stateful_interaction().drag_listener.is_none(), "calling on_drag more than once on the same element is not supported" ); self.stateful_interaction().drag_listener = - Some(Box::new(move |view_state, cursor_offset, cx| { - let drag = listener(view_state, cx); - let drag_handle_view = Some( - cx.build_view(|cx| DragView { - model: cx.model().upgrade().unwrap(), - drag, - }) - .into_any(), - ); - AnyDrag { - render: { - let view = cx.view(); - Box::new(move |cx| { - view.update(cx, |view, cx| drag.render_drag_handle(view, cx)) - }) - }, - drag_handle_view, - cursor_offset, - state: Box::new(drag.state), - state_type: TypeId::of::(), - } + Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag { + view: listener(view_state, cx).into_any(), + cursor_offset, })); self } @@ -419,7 +398,7 @@ pub trait ElementInteraction: 'static + Send { if let Some(drag) = cx.active_drag.take() { for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles { if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) { - if *state_type == drag.state_type + if *state_type == drag.view.entity_type() && group_bounds.contains_point(&mouse_position) { style.refine(&group_drag_style.style); @@ -428,7 +407,8 @@ pub trait ElementInteraction: 'static + Send { } for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles { - if *state_type == drag.state_type && bounds.contains_point(&mouse_position) { + if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position) + { style.refine(drag_over_style); } } @@ -516,7 +496,7 @@ pub trait ElementInteraction: 'static + Send { cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { if let Some(drag_state_type) = - cx.active_drag.as_ref().map(|drag| drag.state_type) + cx.active_drag.as_ref().map(|drag| drag.view.entity_type()) { for (drop_state_type, listener) in &drop_listeners { if *drop_state_type == drag_state_type { @@ -524,7 +504,7 @@ pub trait ElementInteraction: 'static + Send { .active_drag .take() .expect("checked for type drag state type above"); - listener(view, drag.state, cx); + listener(view, drag.view.clone(), cx); cx.notify(); cx.stop_propagation(); } @@ -692,7 +672,7 @@ impl From for StatefulInteraction { } } -type DropListener = dyn Fn(&mut V, AnyBox, &mut ViewContext) + 'static + Send; +type DropListener = dyn Fn(&mut V, AnyView, &mut ViewContext) + 'static + Send; pub struct StatelessInteraction { pub dispatch_context: DispatchContext, @@ -873,7 +853,7 @@ pub struct Drag where R: Fn(&mut V, &mut ViewContext) -> E, V: 'static, - E: Component, + E: Component<()>, { pub state: S, pub render_drag_handle: R, @@ -884,7 +864,7 @@ impl Drag where R: Fn(&mut V, &mut ViewContext) -> E, V: 'static, - E: Component, + E: Component<()>, { pub fn new(state: S, render_drag_handle: R) -> Self { Drag { @@ -1006,6 +986,14 @@ impl Deref for MouseExitEvent { #[derive(Debug, Clone, Default)] pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>); +impl Render for ExternalPaths { + type Element = Div; + + fn render(&mut self, _: &mut ViewContext) -> Self::Element { + div() // Intentionally left empty because the platform will render icons for the dragged files + } +} + #[derive(Debug, Clone)] pub enum FileDropEvent { Entered { diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index 630f2f9864..eef5819361 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -1,9 +1,10 @@ use crate::{ - AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId, - EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, WeakModel, WindowContext, + AnyBox, AnyElement, AnyModel, AppContext, AvailableSpace, BorrowWindow, Bounds, Component, + Element, ElementId, EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, + WeakModel, WindowContext, }; use anyhow::{Context, Result}; -use std::{marker::PhantomData, sync::Arc}; +use std::{any::TypeId, marker::PhantomData, sync::Arc}; pub trait Render: 'static + Sized { type Element: Element + 'static + Send; @@ -38,6 +39,10 @@ impl View { { cx.update_view(self, f) } + + pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V { + self.model.read(cx) + } } impl Clone for View { @@ -178,7 +183,9 @@ impl Element for EraseViewState TypeId; fn entity_id(&self) -> EntityId; + fn model(&self) -> AnyModel; fn initialize(&self, cx: &mut WindowContext) -> AnyBox; fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId; fn paint(&self, bounds: Bounds, element: &mut AnyBox, cx: &mut WindowContext); @@ -188,10 +195,18 @@ impl ViewObject for View where V: Render, { + fn entity_type(&self) -> TypeId { + TypeId::of::() + } + fn entity_id(&self) -> EntityId { self.model.entity_id } + fn model(&self) -> AnyModel { + self.model.clone().into_any() + } + fn initialize(&self, cx: &mut WindowContext) -> AnyBox { cx.with_element_id(self.entity_id(), |_global_id, cx| { self.update(cx, |state, cx| { @@ -225,6 +240,14 @@ where pub struct AnyView(Arc); impl AnyView { + pub fn downcast(self) -> Option> { + self.0.model().downcast().map(|model| View { model }) + } + + pub(crate) fn entity_type(&self) -> TypeId { + self.0.entity_type() + } + pub(crate) fn draw(&self, available_space: Size, cx: &mut WindowContext) { let mut rendered_element = self.0.initialize(cx); let layout_id = self.0.layout(&mut rendered_element, cx); @@ -294,6 +317,18 @@ impl Component for EraseAnyViewState { } } +impl Render for T +where + T: 'static + FnMut(&mut WindowContext) -> E, + E: 'static + Send + Element, +{ + type Element = E; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + (self)(cx) + } +} + impl Element for EraseAnyViewState { type ElementState = AnyBox; diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 86732cd21e..d5e18e1439 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1,11 +1,11 @@ use crate::{ px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, - EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId, GlobalElementId, - GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, - LayoutId, MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, - MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, - PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, + EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, + ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, + MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, + Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakModel, WeakView, WindowOptions, SUBPIXEL_VARIANTS, @@ -891,18 +891,13 @@ impl<'a, 'w> WindowContext<'a, 'w> { root_view.draw(available_space, cx); }); - if let Some(mut active_drag) = self.app.active_drag.take() { + if let Some(active_drag) = self.app.active_drag.take() { self.stack(1, |cx| { let offset = cx.mouse_position() - active_drag.cursor_offset; cx.with_element_offset(Some(offset), |cx| { let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); - if let Some(drag_handle_view) = &mut active_drag.drag_handle_view { - drag_handle_view.draw(available_space, cx); - } - if let Some(render) = &mut active_drag.render { - (render)() - } + active_drag.view.draw(available_space, cx); cx.active_drag = Some(active_drag); }); }); @@ -970,12 +965,12 @@ impl<'a, 'w> WindowContext<'a, 'w> { InputEvent::FileDrop(file_drop) => match file_drop { FileDropEvent::Entered { position, files } => { self.window.mouse_position = position; - self.active_drag.get_or_insert_with(|| AnyDrag { - drag_handle_view: None, - cursor_offset: position, - state: Box::new(files), - state_type: TypeId::of::(), - }); + if self.active_drag.is_none() { + self.active_drag = Some(AnyDrag { + view: self.build_view(|_| files).into_any(), + cursor_offset: position, + }); + } InputEvent::MouseDown(MouseDownEvent { position, button: MouseButton::Left, @@ -1276,21 +1271,17 @@ impl Context for WindowContext<'_, '_> { impl VisualContext for WindowContext<'_, '_> { type ViewContext<'a, 'w, V> = ViewContext<'a, 'w, V>; - /// Builds a new view in the current window. The first argument is a function that builds - /// an entity representing the view's state. It is invoked with a `ViewContext` that provides - /// entity-specific access to the window and application state during construction. The second - /// argument is a render function that returns a component based on the view's state. - fn build_view( + fn build_view( &mut self, build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, - render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, ) -> Self::Result> where - E: crate::Component, V: 'static + Send, { let slot = self.app.entities.reserve(); - let view = View::for_handle(slot.clone(), render); + let view = View { + model: slot.clone(), + }; let mut cx = ViewContext::mutable(&mut *self.app, &mut *self.window, view.downgrade()); let entity = build_view_state(&mut cx); self.entities.insert(slot, entity); @@ -1885,16 +1876,11 @@ impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> { impl VisualContext for ViewContext<'_, '_, V> { type ViewContext<'a, 'w, V2> = ViewContext<'a, 'w, V2>; - fn build_view( + fn build_view( &mut self, - build_view: impl FnOnce(&mut Self::ViewContext<'_, '_, V2>) -> V2, - render: impl Fn(&mut V2, &mut ViewContext<'_, '_, V2>) -> E + Send + 'static, - ) -> Self::Result> - where - E: crate::Component, - V2: 'static + Send, - { - self.window_cx.build_view(build_view, render) + build_view: impl FnOnce(&mut Self::ViewContext<'_, '_, W>) -> W, + ) -> Self::Result> { + self.window_cx.build_view(build_view) } fn update_view( diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index b1db0187d6..f3f6a8d5fb 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/crates/storybook2/src/stories/focus.rs @@ -1,6 +1,6 @@ use gpui2::{ - div, Focusable, KeyBinding, ParentElement, StatelessInteractive, Styled, View, VisualContext, - WindowContext, + div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction, + StatelessInteractive, Styled, View, VisualContext, WindowContext, }; use serde::Deserialize; use theme2::theme; @@ -14,12 +14,10 @@ struct ActionB; #[derive(Clone, Default, PartialEq, Deserialize)] struct ActionC; -pub struct FocusStory { - text: View<()>, -} +pub struct FocusStory {} impl FocusStory { - pub fn view(cx: &mut WindowContext) -> View<()> { + pub fn view(cx: &mut WindowContext) -> View { cx.bind_keys([ KeyBinding::new("cmd-a", ActionA, Some("parent")), KeyBinding::new("cmd-a", ActionB, Some("child-1")), @@ -27,8 +25,16 @@ impl FocusStory { ]); cx.register_action_type::(); cx.register_action_type::(); - let theme = theme(cx); + cx.build_view(move |cx| Self {}) + } +} + +impl Render for FocusStory { + type Element = Div, FocusEnabled>; + + fn render(&mut self, cx: &mut gpui2::ViewContext) -> Self::Element { + let theme = theme(cx); let color_1 = theme.git_created; let color_2 = theme.git_modified; let color_3 = theme.git_deleted; @@ -38,80 +44,73 @@ impl FocusStory { let child_1 = cx.focus_handle(); let child_2 = cx.focus_handle(); - cx.build_view( - |_| (), - move |_, cx| { + div() + .id("parent") + .focusable() + .context("parent") + .on_action(|_, action: &ActionA, phase, cx| { + println!("Action A dispatched on parent during {:?}", phase); + }) + .on_action(|_, action: &ActionB, phase, cx| { + println!("Action B dispatched on parent during {:?}", phase); + }) + .on_focus(|_, _, _| println!("Parent focused")) + .on_blur(|_, _, _| println!("Parent blurred")) + .on_focus_in(|_, _, _| println!("Parent focus_in")) + .on_focus_out(|_, _, _| println!("Parent focus_out")) + .on_key_down(|_, event, phase, _| { + println!("Key down on parent {:?} {:?}", phase, event) + }) + .on_key_up(|_, event, phase, _| println!("Key up on parent {:?} {:?}", phase, event)) + .size_full() + .bg(color_1) + .focus(|style| style.bg(color_2)) + .focus_in(|style| style.bg(color_3)) + .child( div() - .id("parent") - .focusable() - .context("parent") - .on_action(|_, action: &ActionA, phase, cx| { - println!("Action A dispatched on parent during {:?}", phase); - }) + .track_focus(&child_1) + .context("child-1") .on_action(|_, action: &ActionB, phase, cx| { - println!("Action B dispatched on parent during {:?}", phase); + println!("Action B dispatched on child 1 during {:?}", phase); }) - .on_focus(|_, _, _| println!("Parent focused")) - .on_blur(|_, _, _| println!("Parent blurred")) - .on_focus_in(|_, _, _| println!("Parent focus_in")) - .on_focus_out(|_, _, _| println!("Parent focus_out")) + .w_full() + .h_6() + .bg(color_4) + .focus(|style| style.bg(color_5)) + .in_focus(|style| style.bg(color_6)) + .on_focus(|_, _, _| println!("Child 1 focused")) + .on_blur(|_, _, _| println!("Child 1 blurred")) + .on_focus_in(|_, _, _| println!("Child 1 focus_in")) + .on_focus_out(|_, _, _| println!("Child 1 focus_out")) .on_key_down(|_, event, phase, _| { - println!("Key down on parent {:?} {:?}", phase, event) + println!("Key down on child 1 {:?} {:?}", phase, event) }) .on_key_up(|_, event, phase, _| { - println!("Key up on parent {:?} {:?}", phase, event) + println!("Key up on child 1 {:?} {:?}", phase, event) }) - .size_full() - .bg(color_1) - .focus(|style| style.bg(color_2)) - .focus_in(|style| style.bg(color_3)) - .child( - div() - .track_focus(&child_1) - .context("child-1") - .on_action(|_, action: &ActionB, phase, cx| { - println!("Action B dispatched on child 1 during {:?}", phase); - }) - .w_full() - .h_6() - .bg(color_4) - .focus(|style| style.bg(color_5)) - .in_focus(|style| style.bg(color_6)) - .on_focus(|_, _, _| println!("Child 1 focused")) - .on_blur(|_, _, _| println!("Child 1 blurred")) - .on_focus_in(|_, _, _| println!("Child 1 focus_in")) - .on_focus_out(|_, _, _| println!("Child 1 focus_out")) - .on_key_down(|_, event, phase, _| { - println!("Key down on child 1 {:?} {:?}", phase, event) - }) - .on_key_up(|_, event, phase, _| { - println!("Key up on child 1 {:?} {:?}", phase, event) - }) - .child("Child 1"), - ) - .child( - div() - .track_focus(&child_2) - .context("child-2") - .on_action(|_, action: &ActionC, phase, cx| { - println!("Action C dispatched on child 2 during {:?}", phase); - }) - .w_full() - .h_6() - .bg(color_4) - .on_focus(|_, _, _| println!("Child 2 focused")) - .on_blur(|_, _, _| println!("Child 2 blurred")) - .on_focus_in(|_, _, _| println!("Child 2 focus_in")) - .on_focus_out(|_, _, _| println!("Child 2 focus_out")) - .on_key_down(|_, event, phase, _| { - println!("Key down on child 2 {:?} {:?}", phase, event) - }) - .on_key_up(|_, event, phase, _| { - println!("Key up on child 2 {:?} {:?}", phase, event) - }) - .child("Child 2"), - ) - }, - ) + .child("Child 1"), + ) + .child( + div() + .track_focus(&child_2) + .context("child-2") + .on_action(|_, action: &ActionC, phase, cx| { + println!("Action C dispatched on child 2 during {:?}", phase); + }) + .w_full() + .h_6() + .bg(color_4) + .on_focus(|_, _, _| println!("Child 2 focused")) + .on_blur(|_, _, _| println!("Child 2 blurred")) + .on_focus_in(|_, _, _| println!("Child 2 focus_in")) + .on_focus_out(|_, _, _| println!("Child 2 focus_out")) + .on_key_down(|_, event, phase, _| { + println!("Key down on child 2 {:?} {:?}", phase, event) + }) + .on_key_up(|_, event, phase, _| { + println!("Key up on child 2 {:?} {:?}", phase, event) + }) + .child("Child 2"), + ) } } diff --git a/crates/storybook2/src/stories/kitchen_sink.rs b/crates/storybook2/src/stories/kitchen_sink.rs index 406eb33853..cfa91417b6 100644 --- a/crates/storybook2/src/stories/kitchen_sink.rs +++ b/crates/storybook2/src/stories/kitchen_sink.rs @@ -1,26 +1,23 @@ -use gpui2::{AppContext, Context, View}; +use crate::{ + story::Story, + story_selector::{ComponentStory, ElementStory}, +}; +use gpui2::{Div, Render, StatefulInteraction, View, VisualContext}; use strum::IntoEnumIterator; use ui::prelude::*; -use crate::story::Story; -use crate::story_selector::{ComponentStory, ElementStory}; - -pub struct KitchenSinkStory {} +pub struct KitchenSinkStory; impl KitchenSinkStory { - pub fn new() -> Self { - Self {} + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| Self) } +} - pub fn view(cx: &mut AppContext) -> View { - { - let state = cx.build_model(|cx| Self::new()); - let render = Self::render; - View::for_handle(state, render) - } - } +impl Render for KitchenSinkStory { + type Element = Div>; - fn render(&mut self, cx: &mut ViewContext) -> impl Component { + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let element_stories = ElementStory::iter() .map(|selector| selector.story(cx)) .collect::>(); diff --git a/crates/storybook2/src/stories/scroll.rs b/crates/storybook2/src/stories/scroll.rs index 1b8877ef5c..b504a512a6 100644 --- a/crates/storybook2/src/stories/scroll.rs +++ b/crates/storybook2/src/stories/scroll.rs @@ -1,54 +1,54 @@ use gpui2::{ - div, px, Component, ParentElement, SharedString, Styled, View, VisualContext, WindowContext, + div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled, + View, VisualContext, WindowContext, }; use theme2::theme; -pub struct ScrollStory { - text: View<()>, -} +pub struct ScrollStory; impl ScrollStory { - pub fn view(cx: &mut WindowContext) -> View<()> { - cx.build_view(|cx| (), move |_, cx| checkerboard(cx, 1)) + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| ScrollStory) } } -fn checkerboard(cx: &mut WindowContext, depth: usize) -> impl Component -where - S: 'static + Send + Sync, -{ - let theme = theme(cx); - let color_1 = theme.git_created; - let color_2 = theme.git_modified; +impl Render for ScrollStory { + type Element = Div>; - div() - .id("parent") - .bg(theme.background) - .size_full() - .overflow_scroll() - .children((0..10).map(|row| { - div() - .w(px(1000.)) - .h(px(100.)) - .flex() - .flex_row() - .children((0..10).map(|column| { - let id = SharedString::from(format!("{}, {}", row, column)); - let bg = if row % 2 == column % 2 { - color_1 - } else { - color_2 - }; - div().id(id).bg(bg).size(px(100. / depth as f32)).when( - row >= 5 && column >= 5, - |d| { - d.overflow_scroll() - .child(div().size(px(50.)).bg(color_1)) - .child(div().size(px(50.)).bg(color_2)) - .child(div().size(px(50.)).bg(color_1)) - .child(div().size(px(50.)).bg(color_2)) - }, - ) - })) - })) + fn render(&mut self, cx: &mut gpui2::ViewContext) -> Self::Element { + let theme = theme(cx); + let color_1 = theme.git_created; + let color_2 = theme.git_modified; + + div() + .id("parent") + .bg(theme.background) + .size_full() + .overflow_scroll() + .children((0..10).map(|row| { + div() + .w(px(1000.)) + .h(px(100.)) + .flex() + .flex_row() + .children((0..10).map(|column| { + let id = SharedString::from(format!("{}, {}", row, column)); + let bg = if row % 2 == column % 2 { + color_1 + } else { + color_2 + }; + div().id(id).bg(bg).size(px(100. as f32)).when( + row >= 5 && column >= 5, + |d| { + d.overflow_scroll() + .child(div().size(px(50.)).bg(color_1)) + .child(div().size(px(50.)).bg(color_2)) + .child(div().size(px(50.)).bg(color_1)) + .child(div().size(px(50.)).bg(color_2)) + }, + ) + })) + })) + } } diff --git a/crates/storybook2/src/stories/text.rs b/crates/storybook2/src/stories/text.rs index 20e109cfa0..85a9fd51a6 100644 --- a/crates/storybook2/src/stories/text.rs +++ b/crates/storybook2/src/stories/text.rs @@ -1,20 +1,21 @@ -use gpui2::{div, white, ParentElement, Styled, View, VisualContext, WindowContext}; +use gpui2::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext}; -pub struct TextStory { - text: View<()>, -} +pub struct TextStory; impl TextStory { - pub fn view(cx: &mut WindowContext) -> View<()> { - cx.build_view(|cx| (), |_, cx| { - div() - .size_full() - .bg(white()) - .child(concat!( - "The quick brown fox jumps over the lazy dog. ", - "Meanwhile, the lazy dog decided it was time for a change. ", - "He started daily workout routines, ate healthier and became the fastest dog in town.", - )) - }) + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| Self) + } +} + +impl Render for TextStory { + type Element = Div; + + fn render(&mut self, cx: &mut gpui2::ViewContext) -> Self::Element { + div().size_full().bg(white()).child(concat!( + "The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", + )) } } diff --git a/crates/storybook2/src/stories/z_index.rs b/crates/storybook2/src/stories/z_index.rs index 7584d0b129..c0e1456bc0 100644 --- a/crates/storybook2/src/stories/z_index.rs +++ b/crates/storybook2/src/stories/z_index.rs @@ -1,15 +1,16 @@ -use gpui2::{px, rgb, Div, Hsla}; +use gpui2::{px, rgb, Div, Hsla, Render}; use ui::prelude::*; use crate::story::Story; /// A reimplementation of the MDN `z-index` example, found here: /// [https://developer.mozilla.org/en-US/docs/Web/CSS/z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index). -#[derive(Component)] pub struct ZIndexStory; -impl ZIndexStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { +impl Render for ZIndexStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title(cx, "z-index")) .child( diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index d6ff77c5c1..997e45ead7 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -7,7 +7,7 @@ use clap::builder::PossibleValue; use clap::ValueEnum; use gpui2::{AnyView, VisualContext}; use strum::{EnumIter, EnumString, IntoEnumIterator}; -use ui::prelude::*; +use ui::{prelude::*, AvatarStory, ButtonStory, DetailsStory, IconStory, InputStory, LabelStory}; #[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)] #[strum(serialize_all = "snake_case")] @@ -27,18 +27,16 @@ pub enum ElementStory { impl ElementStory { pub fn story(&self, cx: &mut WindowContext) -> AnyView { match self { - Self::Avatar => { cx.build_view(|cx| (), |_, _| ui::AvatarStory.render()) }.into_any(), - Self::Button => { cx.build_view(|cx| (), |_, _| ui::ButtonStory.render()) }.into_any(), - Self::Details => { - { cx.build_view(|cx| (), |_, _| ui::DetailsStory.render()) }.into_any() - } + Self::Avatar => cx.build_view(|_| AvatarStory).into_any(), + Self::Button => cx.build_view(|_| ButtonStory).into_any(), + Self::Details => cx.build_view(|_| DetailsStory).into_any(), Self::Focus => FocusStory::view(cx).into_any(), - Self::Icon => { cx.build_view(|cx| (), |_, _| ui::IconStory.render()) }.into_any(), - Self::Input => { cx.build_view(|cx| (), |_, _| ui::InputStory.render()) }.into_any(), - Self::Label => { cx.build_view(|cx| (), |_, _| ui::LabelStory.render()) }.into_any(), + Self::Icon => cx.build_view(|_| IconStory).into_any(), + Self::Input => cx.build_view(|_| InputStory).into_any(), + Self::Label => cx.build_view(|_| LabelStory).into_any(), Self::Scroll => ScrollStory::view(cx).into_any(), Self::Text => TextStory::view(cx).into_any(), - Self::ZIndex => { cx.build_view(|cx| (), |_, _| ZIndexStory.render()) }.into_any(), + Self::ZIndex => cx.build_view(|_| ZIndexStory).into_any(), } } } @@ -77,69 +75,31 @@ pub enum ComponentStory { impl ComponentStory { pub fn story(&self, cx: &mut WindowContext) -> AnyView { match self { - Self::AssistantPanel => { - { cx.build_view(|cx| (), |_, _| ui::AssistantPanelStory.render()) }.into_any() - } - Self::Buffer => { cx.build_view(|cx| (), |_, _| ui::BufferStory.render()) }.into_any(), - Self::Breadcrumb => { - { cx.build_view(|cx| (), |_, _| ui::BreadcrumbStory.render()) }.into_any() - } - Self::ChatPanel => { - { cx.build_view(|cx| (), |_, _| ui::ChatPanelStory.render()) }.into_any() - } - Self::CollabPanel => { - { cx.build_view(|cx| (), |_, _| ui::CollabPanelStory.render()) }.into_any() - } - Self::CommandPalette => { - { cx.build_view(|cx| (), |_, _| ui::CommandPaletteStory.render()) }.into_any() - } - Self::ContextMenu => { - { cx.build_view(|cx| (), |_, _| ui::ContextMenuStory.render()) }.into_any() - } - Self::Facepile => { - { cx.build_view(|cx| (), |_, _| ui::FacepileStory.render()) }.into_any() - } - Self::Keybinding => { - { cx.build_view(|cx| (), |_, _| ui::KeybindingStory.render()) }.into_any() - } - Self::LanguageSelector => { - { cx.build_view(|cx| (), |_, _| ui::LanguageSelectorStory.render()) }.into_any() - } - Self::MultiBuffer => { - { cx.build_view(|cx| (), |_, _| ui::MultiBufferStory.render()) }.into_any() - } - Self::NotificationsPanel => { - { cx.build_view(|cx| (), |_, _| ui::NotificationsPanelStory.render()) }.into_any() - } - Self::Palette => { - { cx.build_view(|cx| (), |_, _| ui::PaletteStory.render()) }.into_any() - } - Self::Panel => { cx.build_view(|cx| (), |_, _| ui::PanelStory.render()) }.into_any(), - Self::ProjectPanel => { - { cx.build_view(|cx| (), |_, _| ui::ProjectPanelStory.render()) }.into_any() - } - Self::RecentProjects => { - { cx.build_view(|cx| (), |_, _| ui::RecentProjectsStory.render()) }.into_any() - } - Self::Tab => { cx.build_view(|cx| (), |_, _| ui::TabStory.render()) }.into_any(), - Self::TabBar => { cx.build_view(|cx| (), |_, _| ui::TabBarStory.render()) }.into_any(), - Self::Terminal => { - { cx.build_view(|cx| (), |_, _| ui::TerminalStory.render()) }.into_any() - } - Self::ThemeSelector => { - { cx.build_view(|cx| (), |_, _| ui::ThemeSelectorStory.render()) }.into_any() - } + Self::AssistantPanel => cx.build_view(|_| ui::AssistantPanelStory).into_any(), + Self::Buffer => cx.build_view(|_| ui::BufferStory).into_any(), + Self::Breadcrumb => cx.build_view(|_| ui::BreadcrumbStory).into_any(), + Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into_any(), + Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into_any(), + Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into_any(), + Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into_any(), + Self::Facepile => cx.build_view(|_| ui::FacepileStory).into_any(), + Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into_any(), + Self::LanguageSelector => cx.build_view(|_| ui::LanguageSelectorStory).into_any(), + Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into_any(), + Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into_any(), + Self::Palette => cx.build_view(|cx| ui::PaletteStory).into_any(), + Self::Panel => cx.build_view(|cx| ui::PanelStory).into_any(), + Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into_any(), + Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into_any(), + Self::Tab => cx.build_view(|_| ui::TabStory).into_any(), + Self::TabBar => cx.build_view(|_| ui::TabBarStory).into_any(), + Self::Terminal => cx.build_view(|_| ui::TerminalStory).into_any(), + Self::ThemeSelector => cx.build_view(|_| ui::ThemeSelectorStory).into_any(), + Self::Toast => cx.build_view(|_| ui::ToastStory).into_any(), + Self::Toolbar => cx.build_view(|_| ui::ToolbarStory).into_any(), + Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into_any(), + Self::Copilot => cx.build_view(|_| ui::CopilotModalStory).into_any(), Self::TitleBar => ui::TitleBarStory::view(cx).into_any(), - Self::Toast => { cx.build_view(|cx| (), |_, _| ui::ToastStory.render()) }.into_any(), - Self::Toolbar => { - { cx.build_view(|cx| (), |_, _| ui::ToolbarStory.render()) }.into_any() - } - Self::TrafficLights => { - { cx.build_view(|cx| (), |_, _| ui::TrafficLightsStory.render()) }.into_any() - } - Self::Copilot => { - { cx.build_view(|cx| (), |_, _| ui::CopilotModalStory.render()) }.into_any() - } Self::Workspace => ui::WorkspaceStory::view(cx).into_any(), } } diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 584a9abe39..c2903c88e1 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use clap::Parser; use gpui2::{ - div, px, size, AnyView, AppContext, Bounds, ViewContext, VisualContext, WindowBounds, - WindowOptions, + div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext, + WindowBounds, WindowOptions, }; use log::LevelFilter; use settings2::{default_settings, Settings, SettingsStore}; @@ -82,12 +82,7 @@ fn main() { }), ..Default::default() }, - move |cx| { - cx.build_view( - |cx| StoryWrapper::new(selector.story(cx)), - StoryWrapper::render, - ) - }, + move |cx| cx.build_view(|cx| StoryWrapper::new(selector.story(cx))), ); cx.activate(true); @@ -103,8 +98,12 @@ impl StoryWrapper { pub(crate) fn new(story: AnyView) -> Self { Self { story } } +} - fn render(&mut self, cx: &mut ViewContext) -> impl Component { +impl Render for StoryWrapper { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { div() .flex() .flex_col() diff --git a/crates/ui2/src/components/assistant_panel.rs b/crates/ui2/src/components/assistant_panel.rs index 9b2abd5c17..51c123ad9e 100644 --- a/crates/ui2/src/components/assistant_panel.rs +++ b/crates/ui2/src/components/assistant_panel.rs @@ -1,7 +1,6 @@ -use gpui2::{rems, AbsoluteLength}; - use crate::prelude::*; use crate::{Icon, IconButton, Label, Panel, PanelSide}; +use gpui2::{rems, AbsoluteLength}; #[derive(Component)] pub struct AssistantPanel { @@ -76,15 +75,15 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; - - #[derive(Component)] + use crate::Story; + use gpui2::{Div, Render}; pub struct AssistantPanelStory; - impl AssistantPanelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for AssistantPanelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, AssistantPanel>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/breadcrumb.rs b/crates/ui2/src/components/breadcrumb.rs index 4a2cebcb80..6b2dfe1cce 100644 --- a/crates/ui2/src/components/breadcrumb.rs +++ b/crates/ui2/src/components/breadcrumb.rs @@ -73,21 +73,17 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use super::*; + use crate::Story; + use gpui2::Render; use std::str::FromStr; - use crate::Story; - - use super::*; - - #[derive(Component)] pub struct BreadcrumbStory; - impl BreadcrumbStory { - fn render( - self, - view_state: &mut V, - cx: &mut ViewContext, - ) -> impl Component { + impl Render for BreadcrumbStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let theme = theme(cx); Story::container(cx) diff --git a/crates/ui2/src/components/buffer.rs b/crates/ui2/src/components/buffer.rs index 5754397719..33a98b6ea9 100644 --- a/crates/ui2/src/components/buffer.rs +++ b/crates/ui2/src/components/buffer.rs @@ -233,20 +233,19 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::rems; - + use super::*; use crate::{ empty_buffer_example, hello_world_rust_buffer_example, hello_world_rust_buffer_with_status_example, Story, }; + use gpui2::{rems, Div, Render}; - use super::*; - - #[derive(Component)] pub struct BufferStory; - impl BufferStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for BufferStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let theme = theme(cx); Story::container(cx) diff --git a/crates/ui2/src/components/buffer_search.rs b/crates/ui2/src/components/buffer_search.rs index 85a74930ce..c5539f0a4a 100644 --- a/crates/ui2/src/components/buffer_search.rs +++ b/crates/ui2/src/components/buffer_search.rs @@ -1,4 +1,4 @@ -use gpui2::{AppContext, Context, View}; +use gpui2::{Div, Render, View, VisualContext}; use crate::prelude::*; use crate::{h_stack, Icon, IconButton, IconColor, Input}; @@ -21,15 +21,15 @@ impl BufferSearch { cx.notify(); } - pub fn view(cx: &mut AppContext) -> View { - { - let state = cx.build_model(|cx| Self::new()); - let render = Self::render; - View::for_handle(state, render) - } + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| Self::new()) } +} - fn render(&mut self, cx: &mut ViewContext) -> impl Component { +impl Render for BufferSearch { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Div { let theme = theme(cx); h_stack().bg(theme.toolbar).p_2().child( diff --git a/crates/ui2/src/components/chat_panel.rs b/crates/ui2/src/components/chat_panel.rs index d4fddebc1b..f4f1ddf433 100644 --- a/crates/ui2/src/components/chat_panel.rs +++ b/crates/ui2/src/components/chat_panel.rs @@ -108,16 +108,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { use chrono::DateTime; + use gpui2::{Div, Render}; use crate::{Panel, Story}; use super::*; - #[derive(Component)] pub struct ChatPanelStory; - impl ChatPanelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ChatPanelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, ChatPanel>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/collab_panel.rs b/crates/ui2/src/components/collab_panel.rs index d14c1fec1e..a8552c0f23 100644 --- a/crates/ui2/src/components/collab_panel.rs +++ b/crates/ui2/src/components/collab_panel.rs @@ -89,15 +89,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct CollabPanelStory; - impl CollabPanelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for CollabPanelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, CollabPanel>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/command_palette.rs b/crates/ui2/src/components/command_palette.rs index 71ca5bc41b..63db4359e7 100644 --- a/crates/ui2/src/components/command_palette.rs +++ b/crates/ui2/src/components/command_palette.rs @@ -27,15 +27,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; + use crate::Story; use super::*; - #[derive(Component)] pub struct CommandPaletteStory; - impl CommandPaletteStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for CommandPaletteStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, CommandPalette>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index f2a5557f17..812221036a 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -68,15 +68,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::story::Story; - use super::*; + use crate::story::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct ContextMenuStory; - impl ContextMenuStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ContextMenuStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, ContextMenu>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/copilot.rs b/crates/ui2/src/components/copilot.rs index 7a565d0907..51523d48f0 100644 --- a/crates/ui2/src/components/copilot.rs +++ b/crates/ui2/src/components/copilot.rs @@ -25,15 +25,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; + use crate::Story; use super::*; - #[derive(Component)] pub struct CopilotModalStory; - impl CopilotModalStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for CopilotModalStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, CopilotModal>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/editor_pane.rs b/crates/ui2/src/components/editor_pane.rs index 33f9051dc9..8e54d2c2e8 100644 --- a/crates/ui2/src/components/editor_pane.rs +++ b/crates/ui2/src/components/editor_pane.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use gpui2::{AppContext, Context, View}; +use gpui2::{Div, Render, View, VisualContext}; use crate::prelude::*; use crate::{ @@ -20,7 +20,7 @@ pub struct EditorPane { impl EditorPane { pub fn new( - cx: &mut AppContext, + cx: &mut ViewContext, tabs: Vec, path: PathBuf, symbols: Vec, @@ -42,15 +42,15 @@ impl EditorPane { cx.notify(); } - pub fn view(cx: &mut AppContext) -> View { - { - let state = cx.build_model(|cx| hello_world_rust_editor_with_status_example(cx)); - let render = Self::render; - View::for_handle(state, render) - } + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| hello_world_rust_editor_with_status_example(cx)) } +} - fn render(&mut self, cx: &mut ViewContext) -> impl Component { +impl Render for EditorPane { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Div { v_stack() .w_full() .h_full() diff --git a/crates/ui2/src/components/facepile.rs b/crates/ui2/src/components/facepile.rs index ab1ae47139..21dd848a28 100644 --- a/crates/ui2/src/components/facepile.rs +++ b/crates/ui2/src/components/facepile.rs @@ -31,15 +31,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::{static_players, Story}; - use super::*; + use crate::{static_players, Story}; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct FacepileStory; - impl FacepileStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for FacepileStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let players = static_players(); Story::container(cx) diff --git a/crates/ui2/src/components/keybinding.rs b/crates/ui2/src/components/keybinding.rs index ec29e7a95d..455cfe5b59 100644 --- a/crates/ui2/src/components/keybinding.rs +++ b/crates/ui2/src/components/keybinding.rs @@ -158,17 +158,17 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use super::*; + use crate::Story; + use gpui2::{Div, Render}; use itertools::Itertools; - use crate::Story; - - use super::*; - - #[derive(Component)] pub struct KeybindingStory; - impl KeybindingStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for KeybindingStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let all_modifier_permutations = ModifierKey::iter().permutations(2); Story::container(cx) diff --git a/crates/ui2/src/components/language_selector.rs b/crates/ui2/src/components/language_selector.rs index f75dcf3eaf..fa7f5b2bd7 100644 --- a/crates/ui2/src/components/language_selector.rs +++ b/crates/ui2/src/components/language_selector.rs @@ -38,15 +38,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct LanguageSelectorStory; - impl LanguageSelectorStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for LanguageSelectorStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, LanguageSelector>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/multi_buffer.rs b/crates/ui2/src/components/multi_buffer.rs index e5051fc5b8..696fc77a62 100644 --- a/crates/ui2/src/components/multi_buffer.rs +++ b/crates/ui2/src/components/multi_buffer.rs @@ -40,15 +40,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::{hello_world_rust_buffer_example, Story}; - use super::*; + use crate::{hello_world_rust_buffer_example, Story}; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct MultiBufferStory; - impl MultiBufferStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for MultiBufferStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let theme = theme(cx); Story::container(cx) diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index 68fe6d4bd0..6872f116e9 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -48,15 +48,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::{Panel, Story}; - use super::*; + use crate::{Panel, Story}; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct NotificationsPanelStory; - impl NotificationsPanelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for NotificationsPanelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, NotificationsPanel>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/palette.rs b/crates/ui2/src/components/palette.rs index a74c9e3672..e47f6a4cea 100644 --- a/crates/ui2/src/components/palette.rs +++ b/crates/ui2/src/components/palette.rs @@ -152,58 +152,71 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; + use crate::{ModifierKeys, Story}; use super::*; - #[derive(Component)] pub struct PaletteStory; - impl PaletteStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - Story::container(cx) - .child(Story::title_for::<_, Palette>(cx)) - .child(Story::label(cx, "Default")) - .child(Palette::new("palette-1")) - .child(Story::label(cx, "With Items")) - .child( - Palette::new("palette-2") - .placeholder("Execute a command...") - .items(vec![ - PaletteItem::new("theme selector: toggle").keybinding( - Keybinding::new_chord( - ("k".to_string(), ModifierKeys::new().command(true)), - ("t".to_string(), ModifierKeys::new().command(true)), + impl Render for PaletteStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + { + Story::container(cx) + .child(Story::title_for::<_, Palette>(cx)) + .child(Story::label(cx, "Default")) + .child(Palette::new("palette-1")) + .child(Story::label(cx, "With Items")) + .child( + Palette::new("palette-2") + .placeholder("Execute a command...") + .items(vec![ + PaletteItem::new("theme selector: toggle").keybinding( + Keybinding::new_chord( + ("k".to_string(), ModifierKeys::new().command(true)), + ("t".to_string(), ModifierKeys::new().command(true)), + ), ), - ), - PaletteItem::new("assistant: inline assist").keybinding( - Keybinding::new( - "enter".to_string(), - ModifierKeys::new().command(true), + PaletteItem::new("assistant: inline assist").keybinding( + Keybinding::new( + "enter".to_string(), + ModifierKeys::new().command(true), + ), ), - ), - PaletteItem::new("assistant: quote selection").keybinding( - Keybinding::new(">".to_string(), ModifierKeys::new().command(true)), - ), - PaletteItem::new("assistant: toggle focus").keybinding( - Keybinding::new("?".to_string(), ModifierKeys::new().command(true)), - ), - PaletteItem::new("auto update: check"), - PaletteItem::new("auto update: view release notes"), - PaletteItem::new("branches: open recent").keybinding(Keybinding::new( - "b".to_string(), - ModifierKeys::new().command(true).alt(true), - )), - PaletteItem::new("chat panel: toggle focus"), - PaletteItem::new("cli: install"), - PaletteItem::new("client: sign in"), - PaletteItem::new("client: sign out"), - PaletteItem::new("editor: cancel").keybinding(Keybinding::new( - "escape".to_string(), - ModifierKeys::new(), - )), - ]), - ) + PaletteItem::new("assistant: quote selection").keybinding( + Keybinding::new( + ">".to_string(), + ModifierKeys::new().command(true), + ), + ), + PaletteItem::new("assistant: toggle focus").keybinding( + Keybinding::new( + "?".to_string(), + ModifierKeys::new().command(true), + ), + ), + PaletteItem::new("auto update: check"), + PaletteItem::new("auto update: view release notes"), + PaletteItem::new("branches: open recent").keybinding( + Keybinding::new( + "b".to_string(), + ModifierKeys::new().command(true).alt(true), + ), + ), + PaletteItem::new("chat panel: toggle focus"), + PaletteItem::new("cli: install"), + PaletteItem::new("client: sign in"), + PaletteItem::new("client: sign out"), + PaletteItem::new("editor: cancel").keybinding(Keybinding::new( + "escape".to_string(), + ModifierKeys::new(), + )), + ]), + ) + } } } } diff --git a/crates/ui2/src/components/panel.rs b/crates/ui2/src/components/panel.rs index 40129dbd76..12d2207ffd 100644 --- a/crates/ui2/src/components/panel.rs +++ b/crates/ui2/src/components/panel.rs @@ -128,17 +128,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::{Label, Story}; - use super::*; + use crate::{Label, Story}; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct PanelStory; - impl PanelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for PanelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) - .child(Story::title_for::<_, Panel>(cx)) + .child(Story::title_for::<_, Panel>(cx)) .child(Story::label(cx, "Default")) .child( Panel::new("panel", cx).child( diff --git a/crates/ui2/src/components/panes.rs b/crates/ui2/src/components/panes.rs index b692872741..854786ebaa 100644 --- a/crates/ui2/src/components/panes.rs +++ b/crates/ui2/src/components/panes.rs @@ -1,4 +1,4 @@ -use gpui2::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size}; +use gpui2::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size, View}; use smallvec::SmallVec; use crate::prelude::*; @@ -18,13 +18,6 @@ pub struct Pane { children: SmallVec<[AnyElement; 2]>, } -// impl IntoAnyElement for Pane { -// fn into_any(self) -> AnyElement { -// (move |view_state: &mut V, cx: &mut ViewContext<'_, '_, V>| self.render(view_state, cx)) -// .into_any() -// } -// } - impl Pane { pub fn new(id: impl Into, size: Size) -> Self { // Fill is only here for debugging purposes, remove before release @@ -57,8 +50,8 @@ impl Pane { .z_index(1) .id("drag-target") .drag_over::(|d| d.bg(red())) - .on_drop(|_, files: ExternalPaths, _| { - dbg!("dropped files!", files); + .on_drop(|_, files: View, cx| { + dbg!("dropped files!", files.read(cx)); }) .absolute() .inset_0(), diff --git a/crates/ui2/src/components/project_panel.rs b/crates/ui2/src/components/project_panel.rs index 9f15102acc..84c68119fe 100644 --- a/crates/ui2/src/components/project_panel.rs +++ b/crates/ui2/src/components/project_panel.rs @@ -57,15 +57,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::{Panel, Story}; - use super::*; + use crate::{Panel, Story}; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct ProjectPanelStory; - impl ProjectPanelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ProjectPanelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, ProjectPanel>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/recent_projects.rs b/crates/ui2/src/components/recent_projects.rs index cd7c22eecb..d5a9dd1b22 100644 --- a/crates/ui2/src/components/recent_projects.rs +++ b/crates/ui2/src/components/recent_projects.rs @@ -34,15 +34,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct RecentProjectsStory; - impl RecentProjectsStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for RecentProjectsStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, RecentProjects>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index 6133906f27..d784ec0174 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -1,5 +1,6 @@ use crate::prelude::*; use crate::{Icon, IconColor, IconElement, Label, LabelColor}; +use gpui2::{black, red, Div, ElementId, Render, View, VisualContext}; #[derive(Component, Clone)] pub struct Tab { @@ -19,6 +20,14 @@ struct TabDragState { title: String, } +impl Render for TabDragState { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div().w_8().h_4().bg(red()) + } +} + impl Tab { pub fn new(id: impl Into) -> Self { Self { @@ -118,12 +127,10 @@ impl Tab { div() .id(self.id.clone()) - .on_drag(move |_view, _cx| { - Drag::new(drag_state.clone(), |view, cx| div().w_8().h_4().bg(red())) - }) + .on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone())) .drag_over::(|d| d.bg(black())) - .on_drop(|_view, state: TabDragState, cx| { - dbg!(state); + .on_drop(|_view, state: View, cx| { + dbg!(state.read(cx)); }) .px_2() .py_0p5() @@ -160,23 +167,21 @@ impl Tab { } } -use gpui2::{black, red, Drag, ElementId}; #[cfg(feature = "stories")] pub use stories::*; #[cfg(feature = "stories")] mod stories { + use super::*; + use crate::{h_stack, v_stack, Icon, Story}; use strum::IntoEnumIterator; - use crate::{h_stack, v_stack, Icon, Story}; - - use super::*; - - #[derive(Component)] pub struct TabStory; - impl TabStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for TabStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let git_statuses = GitStatus::iter(); let fs_statuses = FileSystemStatus::iter(); diff --git a/crates/ui2/src/components/tab_bar.rs b/crates/ui2/src/components/tab_bar.rs index cffdc09026..da0a41a1bf 100644 --- a/crates/ui2/src/components/tab_bar.rs +++ b/crates/ui2/src/components/tab_bar.rs @@ -92,15 +92,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct TabBarStory; - impl TabBarStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for TabBarStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, TabBar>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/terminal.rs b/crates/ui2/src/components/terminal.rs index b7c8c5d75d..a751d47dfc 100644 --- a/crates/ui2/src/components/terminal.rs +++ b/crates/ui2/src/components/terminal.rs @@ -83,15 +83,15 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; - - #[derive(Component)] + use crate::Story; + use gpui2::{Div, Render}; pub struct TerminalStory; - impl TerminalStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for TerminalStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, Terminal>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/theme_selector.rs b/crates/ui2/src/components/theme_selector.rs index b5fcd17d2f..5c67f1cd3e 100644 --- a/crates/ui2/src/components/theme_selector.rs +++ b/crates/ui2/src/components/theme_selector.rs @@ -39,15 +39,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; + use crate::Story; use super::*; - #[derive(Component)] pub struct ThemeSelectorStory; - impl ThemeSelectorStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ThemeSelectorStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, ThemeSelector>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/title_bar.rs b/crates/ui2/src/components/title_bar.rs index 29622d1395..4b3b125dea 100644 --- a/crates/ui2/src/components/title_bar.rs +++ b/crates/ui2/src/components/title_bar.rs @@ -1,7 +1,7 @@ use std::sync::atomic::AtomicBool; use std::sync::Arc; -use gpui2::{AppContext, Context, ModelContext, View}; +use gpui2::{Div, Render, View, VisualContext}; use crate::prelude::*; use crate::settings::user_settings; @@ -28,7 +28,7 @@ pub struct TitleBar { } impl TitleBar { - pub fn new(cx: &mut ModelContext) -> Self { + pub fn new(cx: &mut ViewContext) -> Self { let is_active = Arc::new(AtomicBool::new(true)); let active = is_active.clone(); @@ -80,15 +80,15 @@ impl TitleBar { cx.notify(); } - pub fn view(cx: &mut AppContext, livestream: Option) -> View { - { - let state = cx.build_model(|cx| Self::new(cx).set_livestream(livestream)); - let render = Self::render; - View::for_handle(state, render) - } + pub fn view(cx: &mut WindowContext, livestream: Option) -> View { + cx.build_view(|cx| Self::new(cx).set_livestream(livestream)) } +} - fn render(&mut self, cx: &mut ViewContext) -> impl Component { +impl Render for TitleBar { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Div { let theme = theme(cx); let settings = user_settings(cx); @@ -187,26 +187,25 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; pub struct TitleBarStory { title_bar: View, } impl TitleBarStory { - pub fn view(cx: &mut AppContext) -> View { - { - let state = cx.build_model(|cx| Self { - title_bar: TitleBar::view(cx, None), - }); - let render = Self::render; - View::for_handle(state, render) - } + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| Self { + title_bar: TitleBar::view(cx, None), + }) } + } - fn render(&mut self, cx: &mut ViewContext) -> impl Component { + impl Render for TitleBarStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Div { Story::container(cx) .child(Story::title_for::<_, TitleBar>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/toast.rs b/crates/ui2/src/components/toast.rs index dca6f7c28a..814e91c498 100644 --- a/crates/ui2/src/components/toast.rs +++ b/crates/ui2/src/components/toast.rs @@ -72,17 +72,20 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; + use crate::{Label, Story}; use super::*; - #[derive(Component)] pub struct ToastStory; - impl ToastStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ToastStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) - .child(Story::title_for::<_, Toast>(cx)) + .child(Story::title_for::<_, Toast>(cx)) .child(Story::label(cx, "Default")) .child(Toast::new(ToastOrigin::Bottom).child(Label::new("label"))) } diff --git a/crates/ui2/src/components/toolbar.rs b/crates/ui2/src/components/toolbar.rs index a833090c1c..4b35e2d9d2 100644 --- a/crates/ui2/src/components/toolbar.rs +++ b/crates/ui2/src/components/toolbar.rs @@ -75,19 +75,22 @@ mod stories { use std::path::PathBuf; use std::str::FromStr; + use gpui2::{Div, Render}; + use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol}; use super::*; - #[derive(Component)] pub struct ToolbarStory; - impl ToolbarStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ToolbarStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let theme = theme(cx); Story::container(cx) - .child(Story::title_for::<_, Toolbar>(cx)) + .child(Story::title_for::<_, Toolbar>(cx)) .child(Story::label(cx, "Default")) .child( Toolbar::new() diff --git a/crates/ui2/src/components/traffic_lights.rs b/crates/ui2/src/components/traffic_lights.rs index 320e7e68fd..8ee19d26f5 100644 --- a/crates/ui2/src/components/traffic_lights.rs +++ b/crates/ui2/src/components/traffic_lights.rs @@ -77,15 +77,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; + use crate::Story; use super::*; - #[derive(Component)] pub struct TrafficLightsStory; - impl TrafficLightsStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for TrafficLightsStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, TrafficLights>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index f2314fb377..78ab6232a8 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use chrono::DateTime; -use gpui2::{px, relative, rems, AppContext, Context, Size, View}; +use gpui2::{px, relative, rems, Div, Render, Size, View, VisualContext}; use crate::{prelude::*, NotificationsPanel}; use crate::{ @@ -44,7 +44,7 @@ pub struct Workspace { } impl Workspace { - pub fn new(cx: &mut AppContext) -> Self { + pub fn new(cx: &mut ViewContext) -> Self { Self { title_bar: TitleBar::view(cx, None), editor_1: EditorPane::view(cx), @@ -170,15 +170,15 @@ impl Workspace { cx.notify(); } - pub fn view(cx: &mut AppContext) -> View { - { - let state = cx.build_model(|cx| Self::new(cx)); - let render = Self::render; - View::for_handle(state, render) - } + pub fn view(cx: &mut WindowContext) -> View { + cx.build_view(|cx| Self::new(cx)) } +} - pub fn render(&mut self, cx: &mut ViewContext) -> impl Component { +impl Render for Workspace { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Div { let theme = theme(cx); // HACK: This should happen inside of `debug_toggle_user_settings`, but @@ -355,9 +355,8 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::VisualContext; - use super::*; + use gpui2::VisualContext; pub struct WorkspaceStory { workspace: View, @@ -365,12 +364,17 @@ mod stories { impl WorkspaceStory { pub fn view(cx: &mut WindowContext) -> View { - cx.build_view( - |cx| Self { - workspace: Workspace::view(cx), - }, - |view, cx| view.workspace.clone(), - ) + cx.build_view(|cx| Self { + workspace: Workspace::view(cx), + }) + } + } + + impl Render for WorkspaceStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div().child(self.workspace.clone()) } } } diff --git a/crates/ui2/src/elements/avatar.rs b/crates/ui2/src/elements/avatar.rs index 87133209a2..f008eeb479 100644 --- a/crates/ui2/src/elements/avatar.rs +++ b/crates/ui2/src/elements/avatar.rs @@ -43,15 +43,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct AvatarStory; - impl AvatarStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for AvatarStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, Avatar>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/elements/button.rs b/crates/ui2/src/elements/button.rs index 15d65dc5d2..d27a0537d8 100644 --- a/crates/ui2/src/elements/button.rs +++ b/crates/ui2/src/elements/button.rs @@ -219,22 +219,21 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::rems; + use super::*; + use crate::{h_stack, v_stack, LabelColor, Story}; + use gpui2::{rems, Div, Render}; use strum::IntoEnumIterator; - use crate::{h_stack, v_stack, LabelColor, Story}; - - use super::*; - - #[derive(Component)] pub struct ButtonStory; - impl ButtonStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for ButtonStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let states = InteractionState::iter(); Story::container(cx) - .child(Story::title_for::<_, Button>(cx)) + .child(Story::title_for::<_, Button>(cx)) .child( div() .flex() diff --git a/crates/ui2/src/elements/details.rs b/crates/ui2/src/elements/details.rs index f89cfb53db..eca7798c82 100644 --- a/crates/ui2/src/elements/details.rs +++ b/crates/ui2/src/elements/details.rs @@ -46,17 +46,18 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::{Button, Story}; - use super::*; + use crate::{Button, Story}; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct DetailsStory; - impl DetailsStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for DetailsStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) - .child(Story::title_for::<_, Details>(cx)) + .child(Story::title_for::<_, Details>(cx)) .child(Story::label(cx, "Default")) .child(Details::new("The quick brown fox jumps over the lazy dog")) .child(Story::label(cx, "With meta")) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index ef36442296..4e4ec2bce7 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -36,7 +36,7 @@ impl IconColor { IconColor::Error => gpui2::red(), IconColor::Warning => gpui2::red(), IconColor::Success => gpui2::red(), - IconColor::Info => gpui2::red() + IconColor::Info => gpui2::red(), } } } @@ -191,17 +191,19 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { + use gpui2::{Div, Render}; use strum::IntoEnumIterator; use crate::Story; use super::*; - #[derive(Component)] pub struct IconStory; - impl IconStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for IconStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let icons = Icon::iter(); Story::container(cx) diff --git a/crates/ui2/src/elements/input.rs b/crates/ui2/src/elements/input.rs index 79a09e0ba2..e9e92dd0a6 100644 --- a/crates/ui2/src/elements/input.rs +++ b/crates/ui2/src/elements/input.rs @@ -112,15 +112,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct InputStory; - impl InputStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for InputStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, Input>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index 6c97819eeb..4d336345fb 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -197,15 +197,16 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use crate::Story; - use super::*; + use crate::Story; + use gpui2::{Div, Render}; - #[derive(Component)] pub struct LabelStory; - impl LabelStory { - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + impl Render for LabelStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .child(Story::title_for::<_, Label>(cx)) .child(Story::label(cx, "Default")) diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 72407b6678..68f1e36b2c 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::str::FromStr; -use gpui2::{AppContext, WindowContext}; +use gpui2::ViewContext; use rand::Rng; use theme2::Theme; @@ -628,7 +628,7 @@ pub fn example_editor_actions() -> Vec { ] } -pub fn empty_editor_example(cx: &mut WindowContext) -> EditorPane { +pub fn empty_editor_example(cx: &mut ViewContext) -> EditorPane { EditorPane::new( cx, static_tabs_example(), @@ -642,7 +642,7 @@ pub fn empty_buffer_example() -> Buffer { Buffer::new("empty-buffer").set_rows(Some(BufferRows::default())) } -pub fn hello_world_rust_editor_example(cx: &mut WindowContext) -> EditorPane { +pub fn hello_world_rust_editor_example(cx: &mut ViewContext) -> EditorPane { let theme = theme(cx); EditorPane::new( @@ -781,7 +781,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { ] } -pub fn hello_world_rust_editor_with_status_example(cx: &mut AppContext) -> EditorPane { +pub fn hello_world_rust_editor_with_status_example(cx: &mut ViewContext) -> EditorPane { let theme = theme(cx); EditorPane::new(