From 08e9b2e8481bb87f3ae76843d1f24f4d5a29f0bc Mon Sep 17 00:00:00 2001 From: KCaverly Date: Mon, 30 Oct 2023 13:32:47 -0400 Subject: [PATCH 01/18] added parsing support for <|S| |E|> spans --- crates/assistant/src/codegen.rs | 110 +++++++++++++++++++++++++------- crates/assistant/src/prompts.rs | 18 +++--- 2 files changed, 96 insertions(+), 32 deletions(-) diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index b6ef6b5cfa..b26b1713b2 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -117,7 +117,7 @@ impl Codegen { let (mut hunks_tx, mut hunks_rx) = mpsc::channel(1); let diff = cx.background().spawn(async move { - let chunks = strip_markdown_codeblock(response.await?); + let chunks = strip_invalid_spans_from_codeblock(response.await?); futures::pin_mut!(chunks); let mut diff = StreamingDiff::new(selected_text.to_string()); @@ -278,12 +278,13 @@ impl Codegen { } } -fn strip_markdown_codeblock( +fn strip_invalid_spans_from_codeblock( stream: impl Stream>, ) -> impl Stream> { let mut first_line = true; let mut buffer = String::new(); - let mut starts_with_fenced_code_block = false; + let mut starts_with_markdown_codeblock = false; + let mut includes_start_or_end_span = false; stream.filter_map(move |chunk| { let chunk = match chunk { Ok(chunk) => chunk, @@ -291,11 +292,31 @@ fn strip_markdown_codeblock( }; buffer.push_str(&chunk); + if buffer.len() > "<|S|".len() && buffer.starts_with("<|S|") { + includes_start_or_end_span = true; + + buffer = buffer + .strip_prefix("<|S|>") + .or_else(|| buffer.strip_prefix("<|S|")) + .unwrap_or(&buffer) + .to_string(); + } else if buffer.ends_with("|E|>") { + includes_start_or_end_span = true; + } else if buffer.starts_with("<|") + || buffer.starts_with("<|S") + || buffer.starts_with("<|S|") + || buffer.ends_with("|") + || buffer.ends_with("|E") + || buffer.ends_with("|E|") + { + return future::ready(None); + } + if first_line { if buffer == "" || buffer == "`" || buffer == "``" { return future::ready(None); } else if buffer.starts_with("```") { - starts_with_fenced_code_block = true; + starts_with_markdown_codeblock = true; if let Some(newline_ix) = buffer.find('\n') { buffer.replace_range(..newline_ix + 1, ""); first_line = false; @@ -305,16 +326,26 @@ fn strip_markdown_codeblock( } } - let text = if starts_with_fenced_code_block { - buffer + let mut text = buffer.to_string(); + if starts_with_markdown_codeblock { + text = text .strip_suffix("\n```\n") - .or_else(|| buffer.strip_suffix("\n```")) - .or_else(|| buffer.strip_suffix("\n``")) - .or_else(|| buffer.strip_suffix("\n`")) - .or_else(|| buffer.strip_suffix('\n')) - .unwrap_or(&buffer) - } else { - &buffer + .or_else(|| text.strip_suffix("\n```")) + .or_else(|| text.strip_suffix("\n``")) + .or_else(|| text.strip_suffix("\n`")) + .or_else(|| text.strip_suffix('\n')) + .unwrap_or(&text) + .to_string(); + } + + if includes_start_or_end_span { + text = text + .strip_suffix("|E|>") + .or_else(|| text.strip_suffix("E|>")) + .or_else(|| text.strip_prefix("|>")) + .or_else(|| text.strip_prefix(">")) + .unwrap_or(&text) + .to_string(); }; if text.contains('\n') { @@ -327,6 +358,7 @@ fn strip_markdown_codeblock( } else { Some(Ok(buffer.clone())) }; + buffer = remainder; future::ready(result) }) @@ -537,50 +569,82 @@ mod tests { } #[gpui::test] - async fn test_strip_markdown_codeblock() { + async fn test_strip_invalid_spans_from_codeblock() { assert_eq!( - strip_markdown_codeblock(chunks("Lorem ipsum dolor", 2)) + strip_invalid_spans_from_codeblock(chunks("Lorem ipsum dolor", 2)) .map(|chunk| chunk.unwrap()) .collect::() .await, "Lorem ipsum dolor" ); assert_eq!( - strip_markdown_codeblock(chunks("```\nLorem ipsum dolor", 2)) + strip_invalid_spans_from_codeblock(chunks("```\nLorem ipsum dolor", 2)) .map(|chunk| chunk.unwrap()) .collect::() .await, "Lorem ipsum dolor" ); assert_eq!( - strip_markdown_codeblock(chunks("```\nLorem ipsum dolor\n```", 2)) + strip_invalid_spans_from_codeblock(chunks("```\nLorem ipsum dolor\n```", 2)) .map(|chunk| chunk.unwrap()) .collect::() .await, "Lorem ipsum dolor" ); assert_eq!( - strip_markdown_codeblock(chunks("```\nLorem ipsum dolor\n```\n", 2)) + strip_invalid_spans_from_codeblock(chunks("```\nLorem ipsum dolor\n```\n", 2)) .map(|chunk| chunk.unwrap()) .collect::() .await, "Lorem ipsum dolor" ); assert_eq!( - strip_markdown_codeblock(chunks("```html\n```js\nLorem ipsum dolor\n```\n```", 2)) - .map(|chunk| chunk.unwrap()) - .collect::() - .await, + strip_invalid_spans_from_codeblock(chunks( + "```html\n```js\nLorem ipsum dolor\n```\n```", + 2 + )) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, "```js\nLorem ipsum dolor\n```" ); assert_eq!( - strip_markdown_codeblock(chunks("``\nLorem ipsum dolor\n```", 2)) + strip_invalid_spans_from_codeblock(chunks("``\nLorem ipsum dolor\n```", 2)) .map(|chunk| chunk.unwrap()) .collect::() .await, "``\nLorem ipsum dolor\n```" ); + assert_eq!( + strip_invalid_spans_from_codeblock(chunks("<|S|Lorem ipsum|E|>", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum" + ); + assert_eq!( + strip_invalid_spans_from_codeblock(chunks("<|S|>Lorem ipsum", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum" + ); + + assert_eq!( + strip_invalid_spans_from_codeblock(chunks("```\n<|S|>Lorem ipsum\n```", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum" + ); + assert_eq!( + strip_invalid_spans_from_codeblock(chunks("```\n<|S|Lorem ipsum|E|>\n```", 2)) + .map(|chunk| chunk.unwrap()) + .collect::() + .await, + "Lorem ipsum" + ); fn chunks(text: &str, size: usize) -> impl Stream> { stream::iter( text.chars() diff --git a/crates/assistant/src/prompts.rs b/crates/assistant/src/prompts.rs index dffcbc2923..66425d31f7 100644 --- a/crates/assistant/src/prompts.rs +++ b/crates/assistant/src/prompts.rs @@ -79,12 +79,12 @@ fn summarize(buffer: &BufferSnapshot, selected_range: Range) -> S if !flushed_selection { // The collapsed node ends after the selection starts, so we'll flush the selection first. summary.extend(buffer.text_for_range(offset..selected_range.start)); - summary.push_str("<|START|"); + summary.push_str("<|S|"); if selected_range.end == selected_range.start { summary.push_str(">"); } else { summary.extend(buffer.text_for_range(selected_range.clone())); - summary.push_str("|END|>"); + summary.push_str("|E|>"); } offset = selected_range.end; flushed_selection = true; @@ -106,12 +106,12 @@ fn summarize(buffer: &BufferSnapshot, selected_range: Range) -> S // Flush selection if we haven't already done so. if !flushed_selection && offset <= selected_range.start { summary.extend(buffer.text_for_range(offset..selected_range.start)); - summary.push_str("<|START|"); + summary.push_str("<|S|"); if selected_range.end == selected_range.start { summary.push_str(">"); } else { summary.extend(buffer.text_for_range(selected_range.clone())); - summary.push_str("|END|>"); + summary.push_str("|E|>"); } offset = selected_range.end; } @@ -259,7 +259,7 @@ pub(crate) mod tests { summarize(&snapshot, Point::new(1, 4)..Point::new(1, 4)), indoc! {" struct X { - <|START|>a: usize, + <|S|>a: usize, b: usize, } @@ -285,7 +285,7 @@ pub(crate) mod tests { impl X { fn new() -> Self { - let <|START|a |END|>= 1; + let <|S|a |E|>= 1; let b = 2; Self { a, b } } @@ -306,7 +306,7 @@ pub(crate) mod tests { } impl X { - <|START|> + <|S|> fn new() -> Self {} pub fn a(&self, param: bool) -> usize {} @@ -332,7 +332,7 @@ pub(crate) mod tests { pub fn b(&self) -> usize {} } - <|START|>"} + <|S|>"} ); // Ensure nested functions get collapsed properly. @@ -368,7 +368,7 @@ pub(crate) mod tests { assert_eq!( summarize(&snapshot, Point::new(0, 0)..Point::new(0, 0)), indoc! {" - <|START|>struct X { + <|S|>struct X { a: usize, b: usize, } From 033d0ae610c62118409841136047b3dbd0a9691f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 22:07:40 +0200 Subject: [PATCH 02/18] Remember default prettier and its plugin installation --- crates/project/src/project.rs | 37 +++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3e5bcef00c..879f061219 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -162,6 +162,7 @@ pub struct Project { copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, + default_prettier_plugins: Option>, prettier_instances: HashMap< (Option, PathBuf), Shared, Arc>>>, @@ -677,6 +678,7 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: Some(node), + default_prettier_plugins: None, prettier_instances: HashMap::default(), } }) @@ -776,6 +778,7 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: None, + default_prettier_plugins: None, prettier_instances: HashMap::default(), }; for worktree in worktrees { @@ -8497,7 +8500,7 @@ impl Project { #[cfg(any(test, feature = "test-support"))] fn install_default_formatters( - &self, + &mut self, _worktree: Option, _new_language: &Language, _language_settings: &LanguageSettings, @@ -8508,7 +8511,7 @@ impl Project { #[cfg(not(any(test, feature = "test-support")))] fn install_default_formatters( - &self, + &mut self, worktree: Option, new_language: &Language, language_settings: &LanguageSettings, @@ -8537,6 +8540,30 @@ impl Project { return Task::ready(Ok(())); }; + let mut plugins_to_install = prettier_plugins; + if let Some(already_installed) = &self.default_prettier_plugins { + plugins_to_install.retain(|plugin| !already_installed.contains(plugin)); + } + if plugins_to_install.is_empty() && self.default_prettier_plugins.is_some() { + return Task::ready(Ok(())); + } + + let previous_plugins = self.default_prettier_plugins.clone(); + self.default_prettier_plugins + .get_or_insert_with(HashSet::default) + .extend(plugins_to_install.iter()); + let (mut install_success_tx, mut install_success_rx) = + futures::channel::mpsc::channel::<()>(1); + cx.spawn(|this, mut cx| async move { + if install_success_rx.next().await.is_none() { + this.update(&mut cx, |this, _| { + log::warn!("Prettier plugin installation did not finish successfully, restoring previous installed plugins {previous_plugins:?}"); + this.default_prettier_plugins = previous_plugins; + }) + } + }) + .detach(); + let default_prettier_dir = util::paths::DEFAULT_PRETTIER_DIR.as_path(); let already_running_prettier = self .prettier_instances @@ -8552,7 +8579,7 @@ impl Project { .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; let packages_to_versions = future::try_join_all( - prettier_plugins + plugins_to_install .iter() .chain(Some(&"prettier")) .map(|package_name| async { @@ -8573,8 +8600,10 @@ impl Project { (package.as_str(), version.as_str()) }).collect::>(); node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; + let installed_packages = !plugins_to_install.is_empty(); + install_success_tx.try_send(()).ok(); - if !prettier_plugins.is_empty() { + if !installed_packages { if let Some(prettier) = already_running_prettier { prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; } From 29a32039bab78bda4caf52fe90ba232203425462 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 22:16:00 +0200 Subject: [PATCH 03/18] Start message numbering during prettier init, log error message text --- crates/prettier/src/prettier_server.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/prettier/src/prettier_server.js b/crates/prettier/src/prettier_server.js index a56c220f20..9967aec50f 100644 --- a/crates/prettier/src/prettier_server.js +++ b/crates/prettier/src/prettier_server.js @@ -55,8 +55,11 @@ async function handleBuffer(prettier) { } // allow concurrent request handling by not `await`ing the message handling promise (async function) handleMessage(message, prettier).catch(e => { - sendResponse({ id: message.id, ...makeError(`error during message handling: ${e}`) }); - }); + const errorMessage = message; + if ((errorMessage.params || {}).text !== undefined) { + errorMessage.params.text = "..snip.."; + } + sendResponse({ id: message.id, ...makeError(`error during message '${JSON.stringify(errorMessage)}' handling: ${e}`) }); }); } } @@ -172,7 +175,7 @@ async function handleMessage(message, prettier) { sendResponse({ id, result: null }); } else if (method === 'initialize') { sendResponse({ - id, + id: id || 0, result: { "capabilities": {} } From fd6f6cc9f8b10d7bd4307e89099b2c1e5b0d31a5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Oct 2023 22:33:44 +0200 Subject: [PATCH 04/18] Return proper full paths for single file workspaces --- crates/project/src/worktree.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index f6fae0c98b..80fd44761c 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -2662,12 +2662,12 @@ impl language::File for File { impl language::LocalFile for File { fn abs_path(&self, cx: &AppContext) -> PathBuf { - self.worktree - .read(cx) - .as_local() - .unwrap() - .abs_path - .join(&self.path) + let worktree_path = &self.worktree.read(cx).as_local().unwrap().abs_path; + if self.path.as_ref() == Path::new("") { + worktree_path.to_path_buf() + } else { + worktree_path.join(&self.path) + } } fn load(&self, cx: &AppContext) -> Task> { From d6abd8a2b4f85d88b4bf2c8ee16a550668f72c0e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 30 Oct 2023 15:32:52 -0700 Subject: [PATCH 05/18] Add missing dev-dependency feature for editor multi_buffer dependency --- crates/editor/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 95d7820063..5113b5e7de 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -80,6 +80,7 @@ util = { path = "../util", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } +multi_buffer = { path = "../multi_buffer", features = ["test-support"] } ctor.workspace = true env_logger.workspace = true From 6ee9beed73dd4c9ed3a13a05bc56c561c566d958 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 31 Oct 2023 11:54:40 +0200 Subject: [PATCH 06/18] Enqueue default prettier installations --- crates/project/src/project.rs | 166 +++++++++++++++++++++------------- 1 file changed, 103 insertions(+), 63 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 879f061219..b38bcd1db2 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -162,13 +162,20 @@ pub struct Project { copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, - default_prettier_plugins: Option>, + #[cfg(not(any(test, feature = "test-support")))] + default_prettier: Option, prettier_instances: HashMap< (Option, PathBuf), Shared, Arc>>>, >, } +#[cfg(not(any(test, feature = "test-support")))] +struct DefaultPrettier { + installation_process: Option>>, + installed_plugins: HashSet<&'static str>, +} + struct DelayedDebounced { task: Option>, cancel_channel: Option>, @@ -678,7 +685,8 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: Some(node), - default_prettier_plugins: None, + #[cfg(not(any(test, feature = "test-support")))] + default_prettier: None, prettier_instances: HashMap::default(), } }) @@ -778,7 +786,8 @@ impl Project { copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), node: None, - default_prettier_plugins: None, + #[cfg(not(any(test, feature = "test-support")))] + default_prettier: None, prettier_instances: HashMap::default(), }; for worktree in worktrees { @@ -8541,76 +8550,107 @@ impl Project { }; let mut plugins_to_install = prettier_plugins; - if let Some(already_installed) = &self.default_prettier_plugins { - plugins_to_install.retain(|plugin| !already_installed.contains(plugin)); - } - if plugins_to_install.is_empty() && self.default_prettier_plugins.is_some() { - return Task::ready(Ok(())); - } - - let previous_plugins = self.default_prettier_plugins.clone(); - self.default_prettier_plugins - .get_or_insert_with(HashSet::default) - .extend(plugins_to_install.iter()); let (mut install_success_tx, mut install_success_rx) = - futures::channel::mpsc::channel::<()>(1); - cx.spawn(|this, mut cx| async move { - if install_success_rx.next().await.is_none() { - this.update(&mut cx, |this, _| { - log::warn!("Prettier plugin installation did not finish successfully, restoring previous installed plugins {previous_plugins:?}"); - this.default_prettier_plugins = previous_plugins; - }) - } - }) - .detach(); + futures::channel::mpsc::channel::>(1); + let new_installation_process = cx + .spawn(|this, mut cx| async move { + if let Some(installed_plugins) = install_success_rx.next().await { + this.update(&mut cx, |this, _| { + let default_prettier = + this.default_prettier + .get_or_insert_with(|| DefaultPrettier { + installation_process: None, + installed_plugins: HashSet::default(), + }); + if !installed_plugins.is_empty() { + log::info!("Installed new prettier plugins: {installed_plugins:?}"); + default_prettier.installed_plugins.extend(installed_plugins); + } + }) + } + }) + .shared(); + let previous_installation_process = + if let Some(default_prettier) = &mut self.default_prettier { + plugins_to_install + .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); + if plugins_to_install.is_empty() { + return Task::ready(Ok(())); + } + std::mem::replace( + &mut default_prettier.installation_process, + Some(new_installation_process.clone()), + ) + } else { + None + }; let default_prettier_dir = util::paths::DEFAULT_PRETTIER_DIR.as_path(); let already_running_prettier = self .prettier_instances .get(&(worktree, default_prettier_dir.to_path_buf())) .cloned(); - let fs = Arc::clone(&self.fs); - cx.background() - .spawn(async move { - let prettier_wrapper_path = default_prettier_dir.join(prettier::PRETTIER_SERVER_FILE); - // method creates parent directory if it doesn't exist - fs.save(&prettier_wrapper_path, &text::Rope::from(prettier::PRETTIER_SERVER_JS), text::LineEnding::Unix).await - .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; - - let packages_to_versions = future::try_join_all( - plugins_to_install - .iter() - .chain(Some(&"prettier")) - .map(|package_name| async { - let returned_package_name = package_name.to_string(); - let latest_version = node.npm_package_latest_version(package_name) - .await - .with_context(|| { - format!("fetching latest npm version for package {returned_package_name}") - })?; - anyhow::Ok((returned_package_name, latest_version)) - }), - ) - .await - .context("fetching latest npm versions")?; - - log::info!("Fetching default prettier and plugins: {packages_to_versions:?}"); - let borrowed_packages = packages_to_versions.iter().map(|(package, version)| { - (package.as_str(), version.as_str()) - }).collect::>(); - node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; - let installed_packages = !plugins_to_install.is_empty(); - install_success_tx.try_send(()).ok(); - - if !installed_packages { - if let Some(prettier) = already_running_prettier { - prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; - } + cx.spawn(|this, mut cx| async move { + if let Some(previous_installation_process) = previous_installation_process { + previous_installation_process.await; + } + let mut everything_was_installed = false; + this.update(&mut cx, |this, _| { + match &mut this.default_prettier { + Some(default_prettier) => { + plugins_to_install + .retain(|plugin| !default_prettier.installed_plugins.contains(plugin)); + everything_was_installed = plugins_to_install.is_empty(); + }, + None => this.default_prettier = Some(DefaultPrettier { installation_process: Some(new_installation_process), installed_plugins: HashSet::default() }), } + }); + if everything_was_installed { + return Ok(()); + } - anyhow::Ok(()) - }) + cx.background() + .spawn(async move { + let prettier_wrapper_path = default_prettier_dir.join(prettier::PRETTIER_SERVER_FILE); + // method creates parent directory if it doesn't exist + fs.save(&prettier_wrapper_path, &text::Rope::from(prettier::PRETTIER_SERVER_JS), text::LineEnding::Unix).await + .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; + + let packages_to_versions = future::try_join_all( + plugins_to_install + .iter() + .chain(Some(&"prettier")) + .map(|package_name| async { + let returned_package_name = package_name.to_string(); + let latest_version = node.npm_package_latest_version(package_name) + .await + .with_context(|| { + format!("fetching latest npm version for package {returned_package_name}") + })?; + anyhow::Ok((returned_package_name, latest_version)) + }), + ) + .await + .context("fetching latest npm versions")?; + + log::info!("Fetching default prettier and plugins: {packages_to_versions:?}"); + let borrowed_packages = packages_to_versions.iter().map(|(package, version)| { + (package.as_str(), version.as_str()) + }).collect::>(); + node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; + let installed_packages = !plugins_to_install.is_empty(); + install_success_tx.try_send(plugins_to_install).ok(); + + if !installed_packages { + if let Some(prettier) = already_running_prettier { + prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; + } + } + + anyhow::Ok(()) + }).await + }) } } From 66b520a5130299dd0e167343a8961adf26f56f0c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 31 Oct 2023 17:17:42 +0100 Subject: [PATCH 07/18] Call initialize on the rendered element on AnyView --- crates/gpui2/src/view.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index ff5f10e722..f1d54e7ae0 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -316,9 +316,12 @@ impl From> for AnyView { initialize: |view, cx| { cx.with_element_id(view.model.entity_id, |_, cx| { let view = view.clone().downcast::().unwrap(); - Box::new(AnyElement::new( - view.update(cx, |view, cx| Render::render(view, cx)), - )) + let element = view.update(cx, |view, cx| { + let mut element = AnyElement::new(view.render(cx)); + element.initialize(view, cx); + element + }); + Box::new(element) }) }, layout: |view, element, cx| { From 244e8ce101cedb56d8c073c5acff99b0cf344b70 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 31 Oct 2023 14:04:03 -0700 Subject: [PATCH 08/18] WIP - make livekit work in GPUI2 --- Cargo.lock | 35 +- crates/call2/Cargo.toml | 6 +- crates/call2/src/participant.rs | 4 +- crates/call2/src/room.rs | 225 +++-- .../LiveKitBridge/Package.resolved | 4 +- crates/live_kit_client2/.cargo/config.toml | 2 + crates/live_kit_client2/Cargo.toml | 71 ++ .../LiveKitBridge/Package.resolved | 52 + .../LiveKitBridge/Package.swift | 27 + .../live_kit_client2/LiveKitBridge/README.md | 3 + .../Sources/LiveKitBridge/LiveKitBridge.swift | 327 ++++++ crates/live_kit_client2/build.rs | 172 ++++ crates/live_kit_client2/examples/test_app.rs | 175 ++++ .../live_kit_client2/src/live_kit_client2.rs | 11 + crates/live_kit_client2/src/prod.rs | 943 ++++++++++++++++++ crates/live_kit_client2/src/test.rs | 647 ++++++++++++ 16 files changed, 2586 insertions(+), 118 deletions(-) create mode 100644 crates/live_kit_client2/.cargo/config.toml create mode 100644 crates/live_kit_client2/Cargo.toml create mode 100644 crates/live_kit_client2/LiveKitBridge/Package.resolved create mode 100644 crates/live_kit_client2/LiveKitBridge/Package.swift create mode 100644 crates/live_kit_client2/LiveKitBridge/README.md create mode 100644 crates/live_kit_client2/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift create mode 100644 crates/live_kit_client2/build.rs create mode 100644 crates/live_kit_client2/examples/test_app.rs create mode 100644 crates/live_kit_client2/src/live_kit_client2.rs create mode 100644 crates/live_kit_client2/src/prod.rs create mode 100644 crates/live_kit_client2/src/test.rs diff --git a/Cargo.lock b/Cargo.lock index 3aca27106c..bb72f5d6ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1169,7 +1169,7 @@ dependencies = [ "futures 0.3.28", "gpui2", "language2", - "live_kit_client", + "live_kit_client2", "log", "media", "postage", @@ -4589,6 +4589,39 @@ dependencies = [ "simplelog", ] +[[package]] +name = "live_kit_client2" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-broadcast", + "async-trait", + "block", + "byteorder", + "bytes 1.5.0", + "cocoa", + "collections", + "core-foundation", + "core-graphics", + "foreign-types", + "futures 0.3.28", + "gpui2", + "hmac 0.12.1", + "jwt", + "live_kit_server", + "log", + "media", + "nanoid", + "objc", + "parking_lot 0.11.2", + "postage", + "serde", + "serde_derive", + "serde_json", + "sha2 0.10.7", + "simplelog", +] + [[package]] name = "live_kit_server" version = "0.1.0" diff --git a/crates/call2/Cargo.toml b/crates/call2/Cargo.toml index f0e47832ed..e918ada3e8 100644 --- a/crates/call2/Cargo.toml +++ b/crates/call2/Cargo.toml @@ -13,7 +13,7 @@ test-support = [ "client2/test-support", "collections/test-support", "gpui2/test-support", - "live_kit_client/test-support", + "live_kit_client2/test-support", "project2/test-support", "util/test-support" ] @@ -24,7 +24,7 @@ client2 = { path = "../client2" } collections = { path = "../collections" } gpui2 = { path = "../gpui2" } log.workspace = true -live_kit_client = { path = "../live_kit_client" } +live_kit_client2 = { path = "../live_kit_client2" } fs2 = { path = "../fs2" } language2 = { path = "../language2" } media = { path = "../media" } @@ -47,6 +47,6 @@ fs2 = { path = "../fs2", features = ["test-support"] } language2 = { path = "../language2", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } gpui2 = { path = "../gpui2", features = ["test-support"] } -live_kit_client = { path = "../live_kit_client", features = ["test-support"] } +live_kit_client2 = { path = "../live_kit_client2", features = ["test-support"] } project2 = { path = "../project2", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } diff --git a/crates/call2/src/participant.rs b/crates/call2/src/participant.rs index 7f3e91dbba..a1837e3ad0 100644 --- a/crates/call2/src/participant.rs +++ b/crates/call2/src/participant.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use client2::ParticipantIndex; use client2::{proto, User}; use gpui2::WeakModel; -pub use live_kit_client::Frame; +pub use live_kit_client2::Frame; use project2::Project; use std::{fmt, sync::Arc}; @@ -51,7 +51,7 @@ pub struct RemoteParticipant { #[derive(Clone)] pub struct RemoteVideoTrack { - pub(crate) live_kit_track: Arc, + pub(crate) live_kit_track: Arc, } unsafe impl Send for RemoteVideoTrack {} diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index b7bac52a8b..f0e0b8de17 100644 --- a/crates/call2/src/room.rs +++ b/crates/call2/src/room.rs @@ -19,7 +19,7 @@ use gpui2::{ AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel, }; use language2::LanguageRegistry; -use live_kit_client::{LocalTrackPublication, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate}; +use live_kit_client2::{LocalTrackPublication, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate}; use postage::{sink::Sink, stream::Stream, watch}; use project2::Project; use settings2::Settings; @@ -59,7 +59,7 @@ pub enum Event { pub struct Room { id: u64, channel_id: Option, - // live_kit: Option, + live_kit: Option, status: RoomStatus, shared_projects: HashSet>, joined_projects: HashSet>, @@ -114,125 +114,130 @@ impl Room { user_store: Model, cx: &mut ModelContext, ) -> Self { - todo!() - // let _live_kit_room = if let Some(connection_info) = live_kit_connection_info { - // let room = live_kit_client::Room::new(); - // let mut status = room.status(); - // // Consume the initial status of the room. - // let _ = status.try_recv(); - // let _maintain_room = cx.spawn(|this, mut cx| async move { - // while let Some(status) = status.next().await { - // let this = if let Some(this) = this.upgrade() { - // this - // } else { - // break; - // }; + let live_kit_room = if let Some(connection_info) = live_kit_connection_info { + let room = live_kit_client2::Room::new(); + let mut status = room.status(); + // Consume the initial status of the room. + let _ = status.try_recv(); + let _maintain_room = cx.spawn(|this, mut cx| async move { + while let Some(status) = status.next().await { + let this = if let Some(this) = this.upgrade() { + this + } else { + break; + }; - // if status == live_kit_client::ConnectionState::Disconnected { - // this.update(&mut cx, |this, cx| this.leave(cx).log_err()) - // .ok(); - // break; - // } - // } - // }); + if status == live_kit_client2::ConnectionState::Disconnected { + this.update(&mut cx, |this, cx| this.leave(cx).log_err()) + .ok(); + break; + } + } + }); - // let mut track_video_changes = room.remote_video_track_updates(); - // let _maintain_video_tracks = cx.spawn(|this, mut cx| async move { - // while let Some(track_change) = track_video_changes.next().await { - // let this = if let Some(this) = this.upgrade() { - // this - // } else { - // break; - // }; + let _maintain_video_tracks = cx.spawn_on_main({ + let room = room.clone(); + move |this, mut cx| async move { + let mut track_video_changes = room.remote_video_track_updates(); + while let Some(track_change) = track_video_changes.next().await { + let this = if let Some(this) = this.upgrade() { + this + } else { + break; + }; - // this.update(&mut cx, |this, cx| { - // this.remote_video_track_updated(track_change, cx).log_err() - // }) - // .ok(); - // } - // }); + this.update(&mut cx, |this, cx| { + this.remote_video_track_updated(track_change, cx).log_err() + }) + .ok(); + } + } + }); - // let mut track_audio_changes = room.remote_audio_track_updates(); - // let _maintain_audio_tracks = cx.spawn(|this, mut cx| async move { - // while let Some(track_change) = track_audio_changes.next().await { - // let this = if let Some(this) = this.upgrade() { - // this - // } else { - // break; - // }; + let _maintain_audio_tracks = cx.spawn_on_main({ + let room = room.clone(); + |this, mut cx| async move { + let mut track_audio_changes = room.remote_audio_track_updates(); + while let Some(track_change) = track_audio_changes.next().await { + let this = if let Some(this) = this.upgrade() { + this + } else { + break; + }; - // this.update(&mut cx, |this, cx| { - // this.remote_audio_track_updated(track_change, cx).log_err() - // }) - // .ok(); - // } - // }); + this.update(&mut cx, |this, cx| { + this.remote_audio_track_updated(track_change, cx).log_err() + }) + .ok(); + } + } + }); - // let connect = room.connect(&connection_info.server_url, &connection_info.token); - // cx.spawn(|this, mut cx| async move { - // connect.await?; + let connect = room.connect(&connection_info.server_url, &connection_info.token); + cx.spawn(|this, mut cx| async move { + connect.await?; - // if !cx.update(|cx| Self::mute_on_join(cx))? { - // this.update(&mut cx, |this, cx| this.share_microphone(cx))? - // .await?; - // } + if !cx.update(|cx| Self::mute_on_join(cx))? { + this.update(&mut cx, |this, cx| this.share_microphone(cx))? + .await?; + } - // anyhow::Ok(()) - // }) - // .detach_and_log_err(cx); + anyhow::Ok(()) + }) + .detach_and_log_err(cx); - // Some(LiveKitRoom { - // room, - // screen_track: LocalTrack::None, - // microphone_track: LocalTrack::None, - // next_publish_id: 0, - // muted_by_user: false, - // deafened: false, - // speaking: false, - // _maintain_room, - // _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], - // }) - // } else { - // None - // }; + Some(LiveKitRoom { + room, + screen_track: LocalTrack::None, + microphone_track: LocalTrack::None, + next_publish_id: 0, + muted_by_user: false, + deafened: false, + speaking: false, + _maintain_room, + _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], + }) + } else { + None + }; - // let maintain_connection = cx.spawn({ - // let client = client.clone(); - // move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err() - // }); + let maintain_connection = cx.spawn({ + let client = client.clone(); + move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err() + }); - // Audio::play_sound(Sound::Joined, cx); + Audio::play_sound(Sound::Joined, cx); - // let (room_update_completed_tx, room_update_completed_rx) = watch::channel(); + let (room_update_completed_tx, room_update_completed_rx) = watch::channel(); - // Self { - // id, - // channel_id, - // // live_kit: live_kit_room, - // status: RoomStatus::Online, - // shared_projects: Default::default(), - // joined_projects: Default::default(), - // participant_user_ids: Default::default(), - // local_participant: Default::default(), - // remote_participants: Default::default(), - // pending_participants: Default::default(), - // pending_call_count: 0, - // client_subscriptions: vec![ - // client.add_message_handler(cx.weak_handle(), Self::handle_room_updated) - // ], - // _subscriptions: vec![ - // cx.on_release(Self::released), - // cx.on_app_quit(Self::app_will_quit), - // ], - // leave_when_empty: false, - // pending_room_update: None, - // client, - // user_store, - // follows_by_leader_id_project_id: Default::default(), - // maintain_connection: Some(maintain_connection), - // room_update_completed_tx, - // room_update_completed_rx, - // } + Self { + id, + channel_id, + live_kit: live_kit_room, + status: RoomStatus::Online, + shared_projects: Default::default(), + joined_projects: Default::default(), + participant_user_ids: Default::default(), + local_participant: Default::default(), + remote_participants: Default::default(), + pending_participants: Default::default(), + pending_call_count: 0, + client_subscriptions: vec![ + client.add_message_handler(cx.weak_model(), Self::handle_room_updated) + ], + _subscriptions: vec![ + cx.on_release(Self::released), + cx.on_app_quit(Self::app_will_quit), + ], + leave_when_empty: false, + pending_room_update: None, + client, + user_store, + follows_by_leader_id_project_id: Default::default(), + maintain_connection: Some(maintain_connection), + room_update_completed_tx, + room_update_completed_rx, + } } pub(crate) fn create( @@ -1518,7 +1523,7 @@ impl Room { } #[cfg(any(test, feature = "test-support"))] - pub fn set_display_sources(&self, sources: Vec) { + pub fn set_display_sources(&self, sources: Vec) { todo!() // self.live_kit // .as_ref() @@ -1529,7 +1534,7 @@ impl Room { } struct LiveKitRoom { - room: Arc, + room: Arc, screen_track: LocalTrack, microphone_track: LocalTrack, /// Tracks whether we're currently in a muted state due to auto-mute from deafening or manual mute performed by user. diff --git a/crates/live_kit_client/LiveKitBridge/Package.resolved b/crates/live_kit_client/LiveKitBridge/Package.resolved index b925bc8f0d..85ae088565 100644 --- a/crates/live_kit_client/LiveKitBridge/Package.resolved +++ b/crates/live_kit_client/LiveKitBridge/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/apple/swift-protobuf.git", "state": { "branch": null, - "revision": "ce20dc083ee485524b802669890291c0d8090170", - "version": "1.22.1" + "revision": "0af9125c4eae12a4973fb66574c53a54962a9e1e", + "version": "1.21.0" } } ] diff --git a/crates/live_kit_client2/.cargo/config.toml b/crates/live_kit_client2/.cargo/config.toml new file mode 100644 index 0000000000..b33fe211bd --- /dev/null +++ b/crates/live_kit_client2/.cargo/config.toml @@ -0,0 +1,2 @@ +[live_kit_client_test] +rustflags = ["-C", "link-args=-ObjC"] diff --git a/crates/live_kit_client2/Cargo.toml b/crates/live_kit_client2/Cargo.toml new file mode 100644 index 0000000000..b5b45a8d45 --- /dev/null +++ b/crates/live_kit_client2/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "live_kit_client2" +version = "0.1.0" +edition = "2021" +description = "Bindings to LiveKit Swift client SDK" +publish = false + +[lib] +path = "src/live_kit_client2.rs" +doctest = false + +[[example]] +name = "test_app" + +[features] +test-support = [ + "async-trait", + "collections/test-support", + "gpui2/test-support", + "live_kit_server", + "nanoid", +] + +[dependencies] +collections = { path = "../collections", optional = true } +gpui2 = { path = "../gpui2", optional = true } +live_kit_server = { path = "../live_kit_server", optional = true } +media = { path = "../media" } + +anyhow.workspace = true +async-broadcast = "0.4" +core-foundation = "0.9.3" +core-graphics = "0.22.3" +futures.workspace = true +log.workspace = true +parking_lot.workspace = true +postage.workspace = true + +async-trait = { workspace = true, optional = true } +nanoid = { version ="0.4", optional = true} + +[dev-dependencies] +collections = { path = "../collections", features = ["test-support"] } +gpui2 = { path = "../gpui2", features = ["test-support"] } +live_kit_server = { path = "../live_kit_server" } +media = { path = "../media" } +nanoid = "0.4" + +anyhow.workspace = true +async-trait.workspace = true +block = "0.1" +bytes = "1.2" +byteorder = "1.4" +cocoa = "0.24" +core-foundation = "0.9.3" +core-graphics = "0.22.3" +foreign-types = "0.3" +futures.workspace = true +hmac = "0.12" +jwt = "0.16" +objc = "0.2" +parking_lot.workspace = true +serde.workspace = true +serde_derive.workspace = true +sha2 = "0.10" +simplelog = "0.9" + +[build-dependencies] +serde.workspace = true +serde_derive.workspace = true +serde_json.workspace = true diff --git a/crates/live_kit_client2/LiveKitBridge/Package.resolved b/crates/live_kit_client2/LiveKitBridge/Package.resolved new file mode 100644 index 0000000000..b925bc8f0d --- /dev/null +++ b/crates/live_kit_client2/LiveKitBridge/Package.resolved @@ -0,0 +1,52 @@ +{ + "object": { + "pins": [ + { + "package": "LiveKit", + "repositoryURL": "https://github.com/livekit/client-sdk-swift.git", + "state": { + "branch": null, + "revision": "7331b813a5ab8a95cfb81fb2b4ed10519428b9ff", + "version": "1.0.12" + } + }, + { + "package": "Promises", + "repositoryURL": "https://github.com/google/promises.git", + "state": { + "branch": null, + "revision": "ec957ccddbcc710ccc64c9dcbd4c7006fcf8b73a", + "version": "2.2.0" + } + }, + { + "package": "WebRTC", + "repositoryURL": "https://github.com/webrtc-sdk/Specs.git", + "state": { + "branch": null, + "revision": "2f6bab30c8df0fe59ab3e58bc99097f757f85f65", + "version": "104.5112.17" + } + }, + { + "package": "swift-log", + "repositoryURL": "https://github.com/apple/swift-log.git", + "state": { + "branch": null, + "revision": "32e8d724467f8fe623624570367e3d50c5638e46", + "version": "1.5.2" + } + }, + { + "package": "SwiftProtobuf", + "repositoryURL": "https://github.com/apple/swift-protobuf.git", + "state": { + "branch": null, + "revision": "ce20dc083ee485524b802669890291c0d8090170", + "version": "1.22.1" + } + } + ] + }, + "version": 1 +} diff --git a/crates/live_kit_client2/LiveKitBridge/Package.swift b/crates/live_kit_client2/LiveKitBridge/Package.swift new file mode 100644 index 0000000000..d7b5c271b9 --- /dev/null +++ b/crates/live_kit_client2/LiveKitBridge/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 5.5 + +import PackageDescription + +let package = Package( + name: "LiveKitBridge", + platforms: [ + .macOS(.v10_15) + ], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "LiveKitBridge", + type: .static, + targets: ["LiveKitBridge"]), + ], + dependencies: [ + .package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.0.12")), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "LiveKitBridge", + dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")]), + ] +) diff --git a/crates/live_kit_client2/LiveKitBridge/README.md b/crates/live_kit_client2/LiveKitBridge/README.md new file mode 100644 index 0000000000..b982c67286 --- /dev/null +++ b/crates/live_kit_client2/LiveKitBridge/README.md @@ -0,0 +1,3 @@ +# LiveKitBridge + +A description of this package. diff --git a/crates/live_kit_client2/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client2/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift new file mode 100644 index 0000000000..5f22acf581 --- /dev/null +++ b/crates/live_kit_client2/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -0,0 +1,327 @@ +import Foundation +import LiveKit +import WebRTC +import ScreenCaptureKit + +class LKRoomDelegate: RoomDelegate { + var data: UnsafeRawPointer + var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void + var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void + var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void + var onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void + var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void + var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void + var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void + + init( + data: UnsafeRawPointer, + onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, + onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void, + onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, + onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void, + onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void, + onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, + onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void) + { + self.data = data + self.onDidDisconnect = onDidDisconnect + self.onDidSubscribeToRemoteAudioTrack = onDidSubscribeToRemoteAudioTrack + self.onDidUnsubscribeFromRemoteAudioTrack = onDidUnsubscribeFromRemoteAudioTrack + self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack + self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack + self.onMuteChangedFromRemoteAudioTrack = onMuteChangedFromRemoteAudioTrack + self.onActiveSpeakersChanged = onActiveSpeakersChanged + } + + func room(_ room: Room, didUpdate connectionState: ConnectionState, oldValue: ConnectionState) { + if connectionState.isDisconnected { + self.onDidDisconnect(self.data) + } + } + + func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) { + if track.kind == .video { + self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque()) + } else if track.kind == .audio { + self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque(), Unmanaged.passUnretained(publication).toOpaque()) + } + } + + func room(_ room: Room, participant: Participant, didUpdate publication: TrackPublication, muted: Bool) { + if publication.kind == .audio { + self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted) + } + } + + func room(_ room: Room, didUpdate speakers: [Participant]) { + guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return } + self.onActiveSpeakersChanged(self.data, speaker_ids) + } + + func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) { + if track.kind == .video { + self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString) + } else if track.kind == .audio { + self.onDidUnsubscribeFromRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString) + } + } +} + +class LKVideoRenderer: NSObject, VideoRenderer { + var data: UnsafeRawPointer + var onFrame: @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool + var onDrop: @convention(c) (UnsafeRawPointer) -> Void + var adaptiveStreamIsEnabled: Bool = false + var adaptiveStreamSize: CGSize = .zero + weak var track: VideoTrack? + + init(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) { + self.data = data + self.onFrame = onFrame + self.onDrop = onDrop + } + + deinit { + self.onDrop(self.data) + } + + func setSize(_ size: CGSize) { + } + + func renderFrame(_ frame: RTCVideoFrame?) { + let buffer = frame?.buffer as? RTCCVPixelBuffer + if let pixelBuffer = buffer?.pixelBuffer { + if !self.onFrame(self.data, pixelBuffer) { + DispatchQueue.main.async { + self.track?.remove(videoRenderer: self) + } + } + } + } +} + +@_cdecl("LKRoomDelegateCreate") +public func LKRoomDelegateCreate( + data: UnsafeRawPointer, + onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, + onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void, + onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, + onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void, + onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void, + onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, + onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void +) -> UnsafeMutableRawPointer { + let delegate = LKRoomDelegate( + data: data, + onDidDisconnect: onDidDisconnect, + onDidSubscribeToRemoteAudioTrack: onDidSubscribeToRemoteAudioTrack, + onDidUnsubscribeFromRemoteAudioTrack: onDidUnsubscribeFromRemoteAudioTrack, + onMuteChangedFromRemoteAudioTrack: onMuteChangedFromRemoteAudioTrack, + onActiveSpeakersChanged: onActiveSpeakerChanged, + onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack, + onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack + ) + return Unmanaged.passRetained(delegate).toOpaque() +} + +@_cdecl("LKRoomCreate") +public func LKRoomCreate(delegate: UnsafeRawPointer) -> UnsafeMutableRawPointer { + let delegate = Unmanaged.fromOpaque(delegate).takeUnretainedValue() + return Unmanaged.passRetained(Room(delegate: delegate)).toOpaque() +} + +@_cdecl("LKRoomConnect") +public func LKRoomConnect(room: UnsafeRawPointer, url: CFString, token: CFString, callback: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, callback_data: UnsafeRawPointer) { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + + room.connect(url as String, token as String).then { _ in + callback(callback_data, UnsafeRawPointer(nil) as! CFString?) + }.catch { error in + callback(callback_data, error.localizedDescription as CFString) + } +} + +@_cdecl("LKRoomDisconnect") +public func LKRoomDisconnect(room: UnsafeRawPointer) { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + room.disconnect() +} + +@_cdecl("LKRoomPublishVideoTrack") +public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer?, CFString?) -> Void, callback_data: UnsafeRawPointer) { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + let track = Unmanaged.fromOpaque(track).takeUnretainedValue() + room.localParticipant?.publishVideoTrack(track: track).then { publication in + callback(callback_data, Unmanaged.passRetained(publication).toOpaque(), nil) + }.catch { error in + callback(callback_data, nil, error.localizedDescription as CFString) + } +} + +@_cdecl("LKRoomPublishAudioTrack") +public func LKRoomPublishAudioTrack(room: UnsafeRawPointer, track: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer?, CFString?) -> Void, callback_data: UnsafeRawPointer) { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + let track = Unmanaged.fromOpaque(track).takeUnretainedValue() + room.localParticipant?.publishAudioTrack(track: track).then { publication in + callback(callback_data, Unmanaged.passRetained(publication).toOpaque(), nil) + }.catch { error in + callback(callback_data, nil, error.localizedDescription as CFString) + } +} + + +@_cdecl("LKRoomUnpublishTrack") +public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawPointer) { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + let _ = room.localParticipant?.unpublish(publication: publication) +} + +@_cdecl("LKRoomAudioTracksForRemoteParticipant") +public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + + for (_, participant) in room.remoteParticipants { + if participant.identity == participantId as String { + return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray? + } + } + + return nil; +} + +@_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant") +public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + + for (_, participant) in room.remoteParticipants { + if participant.identity == participantId as String { + return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray? + } + } + + return nil; +} + +@_cdecl("LKRoomVideoTracksForRemoteParticipant") +public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + + for (_, participant) in room.remoteParticipants { + if participant.identity == participantId as String { + return participant.videoTracks.compactMap { $0.track as? RemoteVideoTrack } as CFArray? + } + } + + return nil; +} + +@_cdecl("LKLocalAudioTrackCreateTrack") +public func LKLocalAudioTrackCreateTrack() -> UnsafeMutableRawPointer { + let track = LocalAudioTrack.createTrack(options: AudioCaptureOptions( + echoCancellation: true, + noiseSuppression: true + )) + + return Unmanaged.passRetained(track).toOpaque() +} + + +@_cdecl("LKCreateScreenShareTrackForDisplay") +public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + let display = Unmanaged.fromOpaque(display).takeUnretainedValue() + let track = LocalVideoTrack.createMacOSScreenShareTrack(source: display, preferredMethod: .legacy) + return Unmanaged.passRetained(track).toOpaque() +} + +@_cdecl("LKVideoRendererCreate") +public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer { + Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque() +} + +@_cdecl("LKVideoTrackAddRenderer") +public func LKVideoTrackAddRenderer(track: UnsafeRawPointer, renderer: UnsafeRawPointer) { + let track = Unmanaged.fromOpaque(track).takeUnretainedValue() as! VideoTrack + let renderer = Unmanaged.fromOpaque(renderer).takeRetainedValue() + renderer.track = track + track.add(videoRenderer: renderer) +} + +@_cdecl("LKRemoteVideoTrackGetSid") +public func LKRemoteVideoTrackGetSid(track: UnsafeRawPointer) -> CFString { + let track = Unmanaged.fromOpaque(track).takeUnretainedValue() + return track.sid! as CFString +} + +@_cdecl("LKRemoteAudioTrackGetSid") +public func LKRemoteAudioTrackGetSid(track: UnsafeRawPointer) -> CFString { + let track = Unmanaged.fromOpaque(track).takeUnretainedValue() + return track.sid! as CFString +} + +@_cdecl("LKDisplaySources") +public func LKDisplaySources(data: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, CFArray?, CFString?) -> Void) { + MacOSScreenCapturer.sources(for: .display, includeCurrentApplication: false, preferredMethod: .legacy).then { displaySources in + callback(data, displaySources as CFArray, nil) + }.catch { error in + callback(data, nil, error.localizedDescription as CFString) + } +} + +@_cdecl("LKLocalTrackPublicationSetMute") +public func LKLocalTrackPublicationSetMute( + publication: UnsafeRawPointer, + muted: Bool, + on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, + callback_data: UnsafeRawPointer +) { + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + + if muted { + publication.mute().then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } + } else { + publication.unmute().then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } + } +} + +@_cdecl("LKRemoteTrackPublicationSetEnabled") +public func LKRemoteTrackPublicationSetEnabled( + publication: UnsafeRawPointer, + enabled: Bool, + on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, + callback_data: UnsafeRawPointer +) { + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + + publication.set(enabled: enabled).then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } +} + +@_cdecl("LKRemoteTrackPublicationIsMuted") +public func LKRemoteTrackPublicationIsMuted( + publication: UnsafeRawPointer +) -> Bool { + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + + return publication.muted +} + +@_cdecl("LKRemoteTrackPublicationGetSid") +public func LKRemoteTrackPublicationGetSid( + publication: UnsafeRawPointer +) -> CFString { + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + + return publication.sid as CFString +} diff --git a/crates/live_kit_client2/build.rs b/crates/live_kit_client2/build.rs new file mode 100644 index 0000000000..1445704b46 --- /dev/null +++ b/crates/live_kit_client2/build.rs @@ -0,0 +1,172 @@ +use serde::Deserialize; +use std::{ + env, + path::{Path, PathBuf}, + process::Command, +}; + +const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge"; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SwiftTargetInfo { + pub triple: String, + pub unversioned_triple: String, + pub module_triple: String, + pub swift_runtime_compatibility_version: String, + #[serde(rename = "librariesRequireRPath")] + pub libraries_require_rpath: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SwiftPaths { + pub runtime_library_paths: Vec, + pub runtime_library_import_paths: Vec, + pub runtime_resource_path: String, +} + +#[derive(Debug, Deserialize)] +pub struct SwiftTarget { + pub target: SwiftTargetInfo, + pub paths: SwiftPaths, +} + +const MACOS_TARGET_VERSION: &str = "10.15.7"; + +fn main() { + if cfg!(not(any(test, feature = "test-support"))) { + let swift_target = get_swift_target(); + + build_bridge(&swift_target); + link_swift_stdlib(&swift_target); + link_webrtc_framework(&swift_target); + + // Register exported Objective-C selectors, protocols, etc when building example binaries. + println!("cargo:rustc-link-arg=-Wl,-ObjC"); + } +} + +fn build_bridge(swift_target: &SwiftTarget) { + println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET"); + println!("cargo:rerun-if-changed={}/Sources", SWIFT_PACKAGE_NAME); + println!( + "cargo:rerun-if-changed={}/Package.swift", + SWIFT_PACKAGE_NAME + ); + println!( + "cargo:rerun-if-changed={}/Package.resolved", + SWIFT_PACKAGE_NAME + ); + + let swift_package_root = swift_package_root(); + let swift_target_folder = swift_target_folder(); + if !Command::new("swift") + .arg("build") + .arg("--disable-automatic-resolution") + .args(["--configuration", &env::var("PROFILE").unwrap()]) + .args(["--triple", &swift_target.target.triple]) + .args(["--build-path".into(), swift_target_folder]) + .current_dir(&swift_package_root) + .status() + .unwrap() + .success() + { + panic!( + "Failed to compile swift package in {}", + swift_package_root.display() + ); + } + + println!( + "cargo:rustc-link-search=native={}", + swift_target.out_dir_path().display() + ); + println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME); +} + +fn link_swift_stdlib(swift_target: &SwiftTarget) { + for path in &swift_target.paths.runtime_library_paths { + println!("cargo:rustc-link-search=native={}", path); + } +} + +fn link_webrtc_framework(swift_target: &SwiftTarget) { + let swift_out_dir_path = swift_target.out_dir_path(); + println!("cargo:rustc-link-lib=framework=WebRTC"); + println!( + "cargo:rustc-link-search=framework={}", + swift_out_dir_path.display() + ); + // Find WebRTC.framework as a sibling of the executable when running tests. + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); + // Find WebRTC.framework in parent directory of the executable when running examples. + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/.."); + + let source_path = swift_out_dir_path.join("WebRTC.framework"); + let deps_dir_path = + PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework"); + let target_dir_path = + PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework"); + copy_dir(&source_path, &deps_dir_path); + copy_dir(&source_path, &target_dir_path); +} + +fn get_swift_target() -> SwiftTarget { + let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + if arch == "aarch64" { + arch = "arm64".into(); + } + let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION); + + let swift_target_info_str = Command::new("swift") + .args(["-target", &target, "-print-target-info"]) + .output() + .unwrap() + .stdout; + + serde_json::from_slice(&swift_target_info_str).unwrap() +} + +fn swift_package_root() -> PathBuf { + env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME) +} + +fn swift_target_folder() -> PathBuf { + env::current_dir() + .unwrap() + .join(format!("../../target/{SWIFT_PACKAGE_NAME}")) +} + +fn copy_dir(source: &Path, destination: &Path) { + assert!( + Command::new("rm") + .arg("-rf") + .arg(destination) + .status() + .unwrap() + .success(), + "could not remove {:?} before copying", + destination + ); + + assert!( + Command::new("cp") + .arg("-R") + .args([source, destination]) + .status() + .unwrap() + .success(), + "could not copy {:?} to {:?}", + source, + destination + ); +} + +impl SwiftTarget { + fn out_dir_path(&self) -> PathBuf { + swift_target_folder() + .join(&self.target.unversioned_triple) + .join(env::var("PROFILE").unwrap()) + } +} diff --git a/crates/live_kit_client2/examples/test_app.rs b/crates/live_kit_client2/examples/test_app.rs new file mode 100644 index 0000000000..2147f6ab8c --- /dev/null +++ b/crates/live_kit_client2/examples/test_app.rs @@ -0,0 +1,175 @@ +// use std::time::Duration; +// todo!() + +// use futures::StreamExt; +// use gpui2::{actions, keymap_matcher::Binding, Menu, MenuItem}; +// use live_kit_client2::{ +// LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room, +// }; +// use live_kit_server::token::{self, VideoGrant}; +// use log::LevelFilter; +// use simplelog::SimpleLogger; + +// actions!(capture, [Quit]); + +fn main() { + // SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); + + // gpui2::App::new(()).unwrap().run(|cx| { + // #[cfg(any(test, feature = "test-support"))] + // println!("USING TEST LIVEKIT"); + + // #[cfg(not(any(test, feature = "test-support")))] + // println!("USING REAL LIVEKIT"); + + // cx.platform().activate(true); + // cx.add_global_action(quit); + + // cx.add_bindings([Binding::new("cmd-q", Quit, None)]); + // cx.set_menus(vec![Menu { + // name: "Zed", + // items: vec![MenuItem::Action { + // name: "Quit", + // action: Box::new(Quit), + // os_action: None, + // }], + // }]); + + // let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap_or("http://localhost:7880".into()); + // let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap_or("devkey".into()); + // let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap_or("secret".into()); + + // cx.spawn(|cx| async move { + // let user_a_token = token::create( + // &live_kit_key, + // &live_kit_secret, + // Some("test-participant-1"), + // VideoGrant::to_join("test-room"), + // ) + // .unwrap(); + // let room_a = Room::new(); + // room_a.connect(&live_kit_url, &user_a_token).await.unwrap(); + + // let user2_token = token::create( + // &live_kit_key, + // &live_kit_secret, + // Some("test-participant-2"), + // VideoGrant::to_join("test-room"), + // ) + // .unwrap(); + // let room_b = Room::new(); + // room_b.connect(&live_kit_url, &user2_token).await.unwrap(); + + // let mut audio_track_updates = room_b.remote_audio_track_updates(); + // let audio_track = LocalAudioTrack::create(); + // let audio_track_publication = room_a.publish_audio_track(audio_track).await.unwrap(); + + // if let RemoteAudioTrackUpdate::Subscribed(track, _) = + // audio_track_updates.next().await.unwrap() + // { + // let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + // assert_eq!(remote_tracks.len(), 1); + // assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1"); + // assert_eq!(track.publisher_id(), "test-participant-1"); + // } else { + // panic!("unexpected message"); + // } + + // audio_track_publication.set_mute(true).await.unwrap(); + + // println!("waiting for mute changed!"); + // if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = + // audio_track_updates.next().await.unwrap() + // { + // let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + // assert_eq!(remote_tracks[0].sid(), track_id); + // assert_eq!(muted, true); + // } else { + // panic!("unexpected message"); + // } + + // audio_track_publication.set_mute(false).await.unwrap(); + + // if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = + // audio_track_updates.next().await.unwrap() + // { + // let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + // assert_eq!(remote_tracks[0].sid(), track_id); + // assert_eq!(muted, false); + // } else { + // panic!("unexpected message"); + // } + + // println!("Pausing for 5 seconds to test audio, make some noise!"); + // let timer = cx.background().timer(Duration::from_secs(5)); + // timer.await; + // let remote_audio_track = room_b + // .remote_audio_tracks("test-participant-1") + // .pop() + // .unwrap(); + // room_a.unpublish_track(audio_track_publication); + + // // Clear out any active speakers changed messages + // let mut next = audio_track_updates.next().await.unwrap(); + // while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } = next { + // println!("Speakers changed: {:?}", speakers); + // next = audio_track_updates.next().await.unwrap(); + // } + + // if let RemoteAudioTrackUpdate::Unsubscribed { + // publisher_id, + // track_id, + // } = next + // { + // assert_eq!(publisher_id, "test-participant-1"); + // assert_eq!(remote_audio_track.sid(), track_id); + // assert_eq!(room_b.remote_audio_tracks("test-participant-1").len(), 0); + // } else { + // panic!("unexpected message"); + // } + + // let mut video_track_updates = room_b.remote_video_track_updates(); + // let displays = room_a.display_sources().await.unwrap(); + // let display = displays.into_iter().next().unwrap(); + + // let local_video_track = LocalVideoTrack::screen_share_for_display(&display); + // let local_video_track_publication = + // room_a.publish_video_track(local_video_track).await.unwrap(); + + // if let RemoteVideoTrackUpdate::Subscribed(track) = + // video_track_updates.next().await.unwrap() + // { + // let remote_video_tracks = room_b.remote_video_tracks("test-participant-1"); + // assert_eq!(remote_video_tracks.len(), 1); + // assert_eq!(remote_video_tracks[0].publisher_id(), "test-participant-1"); + // assert_eq!(track.publisher_id(), "test-participant-1"); + // } else { + // panic!("unexpected message"); + // } + + // let remote_video_track = room_b + // .remote_video_tracks("test-participant-1") + // .pop() + // .unwrap(); + // room_a.unpublish_track(local_video_track_publication); + // if let RemoteVideoTrackUpdate::Unsubscribed { + // publisher_id, + // track_id, + // } = video_track_updates.next().await.unwrap() + // { + // assert_eq!(publisher_id, "test-participant-1"); + // assert_eq!(remote_video_track.sid(), track_id); + // assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0); + // } else { + // panic!("unexpected message"); + // } + + // cx.platform().quit(); + // }) + // .detach(); + // }); +} + +// fn quit(_: &Quit, cx: &mut gpui2::AppContext) { +// cx.platform().quit(); +// } diff --git a/crates/live_kit_client2/src/live_kit_client2.rs b/crates/live_kit_client2/src/live_kit_client2.rs new file mode 100644 index 0000000000..35682382e9 --- /dev/null +++ b/crates/live_kit_client2/src/live_kit_client2.rs @@ -0,0 +1,11 @@ +// #[cfg(not(any(test, feature = "test-support")))] +pub mod prod; + +// #[cfg(not(any(test, feature = "test-support")))] +pub use prod::*; + +// #[cfg(any(test, feature = "test-support"))] +// pub mod test; + +// #[cfg(any(test, feature = "test-support"))] +// pub use test::*; diff --git a/crates/live_kit_client2/src/prod.rs b/crates/live_kit_client2/src/prod.rs new file mode 100644 index 0000000000..65ed8b754f --- /dev/null +++ b/crates/live_kit_client2/src/prod.rs @@ -0,0 +1,943 @@ +use anyhow::{anyhow, Context, Result}; +use core_foundation::{ + array::{CFArray, CFArrayRef}, + base::{CFRelease, CFRetain, TCFType}, + string::{CFString, CFStringRef}, +}; +use futures::{ + channel::{mpsc, oneshot}, + Future, +}; +pub use media::core_video::CVImageBuffer; +use media::core_video::CVImageBufferRef; +use parking_lot::Mutex; +use postage::watch; +use std::{ + ffi::c_void, + sync::{Arc, Weak}, +}; + +// SAFETY: Most live kit types are threadsafe: +// https://github.com/livekit/client-sdk-swift#thread-safety +macro_rules! pointer_type { + ($pointer_name:ident) => { + #[repr(transparent)] + #[derive(Copy, Clone, Debug)] + pub struct $pointer_name(pub *const std::ffi::c_void); + unsafe impl Send for $pointer_name {} + }; +} + +mod swift { + pointer_type!(Room); + pointer_type!(LocalAudioTrack); + pointer_type!(RemoteAudioTrack); + pointer_type!(LocalVideoTrack); + pointer_type!(RemoteVideoTrack); + pointer_type!(LocalTrackPublication); + pointer_type!(RemoteTrackPublication); + pointer_type!(MacOSDisplay); + pointer_type!(RoomDelegate); +} + +extern "C" { + fn LKRoomDelegateCreate( + callback_data: *mut c_void, + on_did_disconnect: extern "C" fn(callback_data: *mut c_void), + on_did_subscribe_to_remote_audio_track: extern "C" fn( + callback_data: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + remote_track: swift::RemoteAudioTrack, + remote_publication: swift::RemoteTrackPublication, + ), + on_did_unsubscribe_from_remote_audio_track: extern "C" fn( + callback_data: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + ), + on_mute_changed_from_remote_audio_track: extern "C" fn( + callback_data: *mut c_void, + track_id: CFStringRef, + muted: bool, + ), + on_active_speakers_changed: extern "C" fn( + callback_data: *mut c_void, + participants: CFArrayRef, + ), + on_did_subscribe_to_remote_video_track: extern "C" fn( + callback_data: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + remote_track: swift::RemoteVideoTrack, + ), + on_did_unsubscribe_from_remote_video_track: extern "C" fn( + callback_data: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + ), + ) -> swift::RoomDelegate; + + fn LKRoomCreate(delegate: swift::RoomDelegate) -> swift::Room; + fn LKRoomConnect( + room: swift::Room, + url: CFStringRef, + token: CFStringRef, + callback: extern "C" fn(*mut c_void, CFStringRef), + callback_data: *mut c_void, + ); + fn LKRoomDisconnect(room: swift::Room); + fn LKRoomPublishVideoTrack( + room: swift::Room, + track: swift::LocalVideoTrack, + callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), + callback_data: *mut c_void, + ); + fn LKRoomPublishAudioTrack( + room: swift::Room, + track: swift::LocalAudioTrack, + callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), + callback_data: *mut c_void, + ); + fn LKRoomUnpublishTrack(room: swift::Room, publication: swift::LocalTrackPublication); + + fn LKRoomAudioTracksForRemoteParticipant( + room: swift::Room, + participant_id: CFStringRef, + ) -> CFArrayRef; + + fn LKRoomAudioTrackPublicationsForRemoteParticipant( + room: swift::Room, + participant_id: CFStringRef, + ) -> CFArrayRef; + + fn LKRoomVideoTracksForRemoteParticipant( + room: swift::Room, + participant_id: CFStringRef, + ) -> CFArrayRef; + + fn LKVideoRendererCreate( + callback_data: *mut c_void, + on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool, + on_drop: extern "C" fn(callback_data: *mut c_void), + ) -> *const c_void; + + fn LKRemoteAudioTrackGetSid(track: swift::RemoteAudioTrack) -> CFStringRef; + fn LKVideoTrackAddRenderer(track: swift::RemoteVideoTrack, renderer: *const c_void); + fn LKRemoteVideoTrackGetSid(track: swift::RemoteVideoTrack) -> CFStringRef; + + fn LKDisplaySources( + callback_data: *mut c_void, + callback: extern "C" fn( + callback_data: *mut c_void, + sources: CFArrayRef, + error: CFStringRef, + ), + ); + fn LKCreateScreenShareTrackForDisplay(display: swift::MacOSDisplay) -> swift::LocalVideoTrack; + fn LKLocalAudioTrackCreateTrack() -> swift::LocalAudioTrack; + + fn LKLocalTrackPublicationSetMute( + publication: swift::LocalTrackPublication, + muted: bool, + on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), + callback_data: *mut c_void, + ); + + fn LKRemoteTrackPublicationSetEnabled( + publication: swift::RemoteTrackPublication, + enabled: bool, + on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), + callback_data: *mut c_void, + ); + + fn LKRemoteTrackPublicationIsMuted(publication: swift::RemoteTrackPublication) -> bool; + fn LKRemoteTrackPublicationGetSid(publication: swift::RemoteTrackPublication) -> CFStringRef; +} + +pub type Sid = String; + +#[derive(Clone, Eq, PartialEq)] +pub enum ConnectionState { + Disconnected, + Connected { url: String, token: String }, +} + +pub struct Room { + native_room: Mutex, + connection: Mutex<( + watch::Sender, + watch::Receiver, + )>, + remote_audio_track_subscribers: Mutex>>, + remote_video_track_subscribers: Mutex>>, + _delegate: Mutex, +} + +trait AssertSendSync: Send {} +impl AssertSendSync for Room {} + +impl Room { + pub fn new() -> Arc { + Arc::new_cyclic(|weak_room| { + let delegate = RoomDelegate::new(weak_room.clone()); + Self { + native_room: Mutex::new(unsafe { LKRoomCreate(delegate.native_delegate) }), + connection: Mutex::new(watch::channel_with(ConnectionState::Disconnected)), + remote_audio_track_subscribers: Default::default(), + remote_video_track_subscribers: Default::default(), + _delegate: Mutex::new(delegate), + } + }) + } + + pub fn status(&self) -> watch::Receiver { + self.connection.lock().1.clone() + } + + pub fn connect(self: &Arc, url: &str, token: &str) -> impl Future> { + let url = CFString::new(url); + let token = CFString::new(token); + let (did_connect, tx, rx) = Self::build_done_callback(); + unsafe { + LKRoomConnect( + *self.native_room.lock(), + url.as_concrete_TypeRef(), + token.as_concrete_TypeRef(), + did_connect, + tx, + ) + } + + let this = self.clone(); + let url = url.to_string(); + let token = token.to_string(); + async move { + rx.await.unwrap().context("error connecting to room")?; + *this.connection.lock().0.borrow_mut() = ConnectionState::Connected { url, token }; + Ok(()) + } + } + + fn did_disconnect(&self) { + *self.connection.lock().0.borrow_mut() = ConnectionState::Disconnected; + } + + pub fn display_sources(self: &Arc) -> impl Future>> { + extern "C" fn callback(tx: *mut c_void, sources: CFArrayRef, error: CFStringRef) { + unsafe { + let tx = Box::from_raw(tx as *mut oneshot::Sender>>); + + if sources.is_null() { + let _ = tx.send(Err(anyhow!("{}", CFString::wrap_under_get_rule(error)))); + } else { + let sources = CFArray::wrap_under_get_rule(sources) + .into_iter() + .map(|source| MacOSDisplay::new(swift::MacOSDisplay(*source))) + .collect(); + + let _ = tx.send(Ok(sources)); + } + } + } + + let (tx, rx) = oneshot::channel(); + + unsafe { + LKDisplaySources(Box::into_raw(Box::new(tx)) as *mut _, callback); + } + + async move { rx.await.unwrap() } + } + + pub fn publish_video_track( + self: &Arc, + track: LocalVideoTrack, + ) -> impl Future> { + let (tx, rx) = oneshot::channel::>(); + extern "C" fn callback( + tx: *mut c_void, + publication: swift::LocalTrackPublication, + error: CFStringRef, + ) { + let tx = + unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; + if error.is_null() { + let _ = tx.send(Ok(LocalTrackPublication::new(publication))); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + let _ = tx.send(Err(anyhow!(error))); + } + } + unsafe { + LKRoomPublishVideoTrack( + *self.native_room.lock(), + track.0, + callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ); + } + async { rx.await.unwrap().context("error publishing video track") } + } + + pub fn publish_audio_track( + self: &Arc, + track: LocalAudioTrack, + ) -> impl Future> { + let (tx, rx) = oneshot::channel::>(); + extern "C" fn callback( + tx: *mut c_void, + publication: swift::LocalTrackPublication, + error: CFStringRef, + ) { + let tx = + unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; + if error.is_null() { + let _ = tx.send(Ok(LocalTrackPublication::new(publication))); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + let _ = tx.send(Err(anyhow!(error))); + } + } + unsafe { + LKRoomPublishAudioTrack( + *self.native_room.lock(), + track.0, + callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ); + } + async { rx.await.unwrap().context("error publishing audio track") } + } + + pub fn unpublish_track(&self, publication: LocalTrackPublication) { + unsafe { + LKRoomUnpublishTrack(*self.native_room.lock(), publication.0); + } + } + + pub fn remote_video_tracks(&self, participant_id: &str) -> Vec> { + unsafe { + let tracks = LKRoomVideoTracksForRemoteParticipant( + *self.native_room.lock(), + CFString::new(participant_id).as_concrete_TypeRef(), + ); + + if tracks.is_null() { + Vec::new() + } else { + let tracks = CFArray::wrap_under_get_rule(tracks); + tracks + .into_iter() + .map(|native_track| { + let native_track = swift::RemoteVideoTrack(*native_track); + let id = + CFString::wrap_under_get_rule(LKRemoteVideoTrackGetSid(native_track)) + .to_string(); + Arc::new(RemoteVideoTrack::new( + native_track, + id, + participant_id.into(), + )) + }) + .collect() + } + } + } + + pub fn remote_audio_tracks(&self, participant_id: &str) -> Vec> { + unsafe { + let tracks = LKRoomAudioTracksForRemoteParticipant( + *self.native_room.lock(), + CFString::new(participant_id).as_concrete_TypeRef(), + ); + + if tracks.is_null() { + Vec::new() + } else { + let tracks = CFArray::wrap_under_get_rule(tracks); + tracks + .into_iter() + .map(|native_track| { + let native_track = swift::RemoteAudioTrack(*native_track); + let id = + CFString::wrap_under_get_rule(LKRemoteAudioTrackGetSid(native_track)) + .to_string(); + Arc::new(RemoteAudioTrack::new( + native_track, + id, + participant_id.into(), + )) + }) + .collect() + } + } + } + + pub fn remote_audio_track_publications( + &self, + participant_id: &str, + ) -> Vec> { + unsafe { + let tracks = LKRoomAudioTrackPublicationsForRemoteParticipant( + *self.native_room.lock(), + CFString::new(participant_id).as_concrete_TypeRef(), + ); + + if tracks.is_null() { + Vec::new() + } else { + let tracks = CFArray::wrap_under_get_rule(tracks); + tracks + .into_iter() + .map(|native_track_publication| { + let native_track_publication = + swift::RemoteTrackPublication(*native_track_publication); + Arc::new(RemoteTrackPublication::new(native_track_publication)) + }) + .collect() + } + } + } + + pub fn remote_audio_track_updates(&self) -> mpsc::UnboundedReceiver { + let (tx, rx) = mpsc::unbounded(); + self.remote_audio_track_subscribers.lock().push(tx); + rx + } + + pub fn remote_video_track_updates(&self) -> mpsc::UnboundedReceiver { + let (tx, rx) = mpsc::unbounded(); + self.remote_video_track_subscribers.lock().push(tx); + rx + } + + fn did_subscribe_to_remote_audio_track( + &self, + track: RemoteAudioTrack, + publication: RemoteTrackPublication, + ) { + let track = Arc::new(track); + let publication = Arc::new(publication); + self.remote_audio_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed( + track.clone(), + publication.clone(), + )) + .is_ok() + }); + } + + fn did_unsubscribe_from_remote_audio_track(&self, publisher_id: String, track_id: String) { + self.remote_audio_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::Unsubscribed { + publisher_id: publisher_id.clone(), + track_id: track_id.clone(), + }) + .is_ok() + }); + } + + fn mute_changed_from_remote_audio_track(&self, track_id: String, muted: bool) { + self.remote_audio_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::MuteChanged { + track_id: track_id.clone(), + muted, + }) + .is_ok() + }); + } + + // A vec of publisher IDs + fn active_speakers_changed(&self, speakers: Vec) { + self.remote_audio_track_subscribers + .lock() + .retain(move |tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::ActiveSpeakersChanged { + speakers: speakers.clone(), + }) + .is_ok() + }); + } + + fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { + let track = Arc::new(track); + self.remote_video_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteVideoTrackUpdate::Subscribed(track.clone())) + .is_ok() + }); + } + + fn did_unsubscribe_from_remote_video_track(&self, publisher_id: String, track_id: String) { + self.remote_video_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteVideoTrackUpdate::Unsubscribed { + publisher_id: publisher_id.clone(), + track_id: track_id.clone(), + }) + .is_ok() + }); + } + + fn build_done_callback() -> ( + extern "C" fn(*mut c_void, CFStringRef), + *mut c_void, + oneshot::Receiver>, + ) { + let (tx, rx) = oneshot::channel(); + extern "C" fn done_callback(tx: *mut c_void, error: CFStringRef) { + let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; + if error.is_null() { + let _ = tx.send(Ok(())); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + let _ = tx.send(Err(anyhow!(error))); + } + } + ( + done_callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + rx, + ) + } +} + +impl Drop for Room { + fn drop(&mut self) { + unsafe { + let native_room = &*self.native_room.lock(); + LKRoomDisconnect(*native_room); + CFRelease(native_room.0); + } + } +} + +struct RoomDelegate { + native_delegate: swift::RoomDelegate, + _weak_room: Weak, +} + +impl RoomDelegate { + fn new(weak_room: Weak) -> Self { + let native_delegate = unsafe { + LKRoomDelegateCreate( + weak_room.as_ptr() as *mut c_void, + Self::on_did_disconnect, + Self::on_did_subscribe_to_remote_audio_track, + Self::on_did_unsubscribe_from_remote_audio_track, + Self::on_mute_change_from_remote_audio_track, + Self::on_active_speakers_changed, + Self::on_did_subscribe_to_remote_video_track, + Self::on_did_unsubscribe_from_remote_video_track, + ) + }; + Self { + native_delegate, + _weak_room: weak_room, + } + } + + extern "C" fn on_did_disconnect(room: *mut c_void) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + if let Some(room) = room.upgrade() { + room.did_disconnect(); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_did_subscribe_to_remote_audio_track( + room: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + track: swift::RemoteAudioTrack, + publication: swift::RemoteTrackPublication, + ) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; + let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; + let track = RemoteAudioTrack::new(track, track_id, publisher_id); + let publication = RemoteTrackPublication::new(publication); + if let Some(room) = room.upgrade() { + room.did_subscribe_to_remote_audio_track(track, publication); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_did_unsubscribe_from_remote_audio_track( + room: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + ) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; + let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; + if let Some(room) = room.upgrade() { + room.did_unsubscribe_from_remote_audio_track(publisher_id, track_id); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_mute_change_from_remote_audio_track( + room: *mut c_void, + track_id: CFStringRef, + muted: bool, + ) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; + if let Some(room) = room.upgrade() { + room.mute_changed_from_remote_audio_track(track_id, muted); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_active_speakers_changed(room: *mut c_void, participants: CFArrayRef) { + if participants.is_null() { + return; + } + + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let speakers = unsafe { + CFArray::wrap_under_get_rule(participants) + .into_iter() + .map( + |speaker: core_foundation::base::ItemRef<'_, *const c_void>| { + CFString::wrap_under_get_rule(*speaker as CFStringRef).to_string() + }, + ) + .collect() + }; + + if let Some(room) = room.upgrade() { + room.active_speakers_changed(speakers); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_did_subscribe_to_remote_video_track( + room: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + track: swift::RemoteVideoTrack, + ) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; + let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; + let track = RemoteVideoTrack::new(track, track_id, publisher_id); + if let Some(room) = room.upgrade() { + room.did_subscribe_to_remote_video_track(track); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_did_unsubscribe_from_remote_video_track( + room: *mut c_void, + publisher_id: CFStringRef, + track_id: CFStringRef, + ) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; + let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; + if let Some(room) = room.upgrade() { + room.did_unsubscribe_from_remote_video_track(publisher_id, track_id); + } + let _ = Weak::into_raw(room); + } +} + +impl Drop for RoomDelegate { + fn drop(&mut self) { + unsafe { + CFRelease(self.native_delegate.0); + } + } +} + +pub struct LocalAudioTrack(swift::LocalAudioTrack); + +impl LocalAudioTrack { + pub fn create() -> Self { + Self(unsafe { LKLocalAudioTrackCreateTrack() }) + } +} + +impl Drop for LocalAudioTrack { + fn drop(&mut self) { + unsafe { CFRelease(self.0 .0) } + } +} + +pub struct LocalVideoTrack(swift::LocalVideoTrack); + +impl LocalVideoTrack { + pub fn screen_share_for_display(display: &MacOSDisplay) -> Self { + Self(unsafe { LKCreateScreenShareTrackForDisplay(display.0) }) + } +} + +impl Drop for LocalVideoTrack { + fn drop(&mut self) { + unsafe { CFRelease(self.0 .0) } + } +} + +pub struct LocalTrackPublication(swift::LocalTrackPublication); + +impl LocalTrackPublication { + pub fn new(native_track_publication: swift::LocalTrackPublication) -> Self { + unsafe { + CFRetain(native_track_publication.0); + } + Self(native_track_publication) + } + + pub fn set_mute(&self, muted: bool) -> impl Future> { + let (tx, rx) = futures::channel::oneshot::channel(); + + extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { + let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; + if error.is_null() { + tx.send(Ok(())).ok(); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + tx.send(Err(anyhow!(error))).ok(); + } + } + + unsafe { + LKLocalTrackPublicationSetMute( + self.0, + muted, + complete_callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ) + } + + async move { rx.await.unwrap() } + } +} + +impl Drop for LocalTrackPublication { + fn drop(&mut self) { + unsafe { CFRelease(self.0 .0) } + } +} + +pub struct RemoteTrackPublication { + native_publication: Mutex, +} + +impl RemoteTrackPublication { + pub fn new(native_track_publication: swift::RemoteTrackPublication) -> Self { + unsafe { + CFRetain(native_track_publication.0); + } + Self { + native_publication: Mutex::new(native_track_publication), + } + } + + pub fn sid(&self) -> String { + unsafe { + CFString::wrap_under_get_rule(LKRemoteTrackPublicationGetSid( + *self.native_publication.lock(), + )) + .to_string() + } + } + + pub fn is_muted(&self) -> bool { + unsafe { LKRemoteTrackPublicationIsMuted(*self.native_publication.lock()) } + } + + pub fn set_enabled(&self, enabled: bool) -> impl Future> { + let (tx, rx) = futures::channel::oneshot::channel(); + + extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { + let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; + if error.is_null() { + tx.send(Ok(())).ok(); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + tx.send(Err(anyhow!(error))).ok(); + } + } + + unsafe { + LKRemoteTrackPublicationSetEnabled( + *self.native_publication.lock(), + enabled, + complete_callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ) + } + + async move { rx.await.unwrap() } + } +} + +impl Drop for RemoteTrackPublication { + fn drop(&mut self) { + unsafe { CFRelease((*self.native_publication.lock()).0) } + } +} + +#[derive(Debug)] +pub struct RemoteAudioTrack { + native_track: Mutex, + sid: Sid, + publisher_id: String, +} + +impl RemoteAudioTrack { + fn new(native_track: swift::RemoteAudioTrack, sid: Sid, publisher_id: String) -> Self { + unsafe { + CFRetain(native_track.0); + } + Self { + native_track: Mutex::new(native_track), + sid, + publisher_id, + } + } + + pub fn sid(&self) -> &str { + &self.sid + } + + pub fn publisher_id(&self) -> &str { + &self.publisher_id + } + + pub fn enable(&self) -> impl Future> { + async { Ok(()) } + } + + pub fn disable(&self) -> impl Future> { + async { Ok(()) } + } +} + +impl Drop for RemoteAudioTrack { + fn drop(&mut self) { + unsafe { CFRelease(self.native_track.lock().0) } + } +} + +#[derive(Debug)] +pub struct RemoteVideoTrack { + native_track: Mutex, + sid: Sid, + publisher_id: String, +} + +impl RemoteVideoTrack { + fn new(native_track: swift::RemoteVideoTrack, sid: Sid, publisher_id: String) -> Self { + unsafe { + CFRetain(native_track.0); + } + Self { + native_track: Mutex::new(native_track), + sid, + publisher_id, + } + } + + pub fn sid(&self) -> &str { + &self.sid + } + + pub fn publisher_id(&self) -> &str { + &self.publisher_id + } + + pub fn frames(&self) -> async_broadcast::Receiver { + extern "C" fn on_frame(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool { + unsafe { + let tx = Box::from_raw(callback_data as *mut async_broadcast::Sender); + let buffer = CVImageBuffer::wrap_under_get_rule(frame); + let result = tx.try_broadcast(Frame(buffer)); + let _ = Box::into_raw(tx); + match result { + Ok(_) => true, + Err(async_broadcast::TrySendError::Closed(_)) + | Err(async_broadcast::TrySendError::Inactive(_)) => { + log::warn!("no active receiver for frame"); + false + } + Err(async_broadcast::TrySendError::Full(_)) => { + log::warn!("skipping frame as receiver is not keeping up"); + true + } + } + } + } + + extern "C" fn on_drop(callback_data: *mut c_void) { + unsafe { + let _ = Box::from_raw(callback_data as *mut async_broadcast::Sender); + } + } + + let (tx, rx) = async_broadcast::broadcast(64); + unsafe { + let renderer = LKVideoRendererCreate( + Box::into_raw(Box::new(tx)) as *mut c_void, + on_frame, + on_drop, + ); + LKVideoTrackAddRenderer(*self.native_track.lock(), renderer); + rx + } + } +} + +impl Drop for RemoteVideoTrack { + fn drop(&mut self) { + unsafe { CFRelease(self.native_track.lock().0) } + } +} + +pub enum RemoteVideoTrackUpdate { + Subscribed(Arc), + Unsubscribed { publisher_id: Sid, track_id: Sid }, +} + +pub enum RemoteAudioTrackUpdate { + ActiveSpeakersChanged { speakers: Vec }, + MuteChanged { track_id: Sid, muted: bool }, + Subscribed(Arc, Arc), + Unsubscribed { publisher_id: Sid, track_id: Sid }, +} + +pub struct MacOSDisplay(swift::MacOSDisplay); + +impl MacOSDisplay { + fn new(ptr: swift::MacOSDisplay) -> Self { + unsafe { + CFRetain(ptr.0); + } + Self(ptr) + } +} + +impl Drop for MacOSDisplay { + fn drop(&mut self) { + unsafe { CFRelease(self.0 .0) } + } +} + +#[derive(Clone)] +pub struct Frame(CVImageBuffer); + +impl Frame { + pub fn width(&self) -> usize { + self.0.width() + } + + pub fn height(&self) -> usize { + self.0.height() + } + + pub fn image(&self) -> CVImageBuffer { + self.0.clone() + } +} diff --git a/crates/live_kit_client2/src/test.rs b/crates/live_kit_client2/src/test.rs new file mode 100644 index 0000000000..7185c11fa8 --- /dev/null +++ b/crates/live_kit_client2/src/test.rs @@ -0,0 +1,647 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use collections::{BTreeMap, HashMap}; +use futures::Stream; +use gpui2::Executor; +use live_kit_server::token; +use media::core_video::CVImageBuffer; +use parking_lot::Mutex; +use postage::watch; +use std::{future::Future, mem, sync::Arc}; + +static SERVERS: Mutex>> = Mutex::new(BTreeMap::new()); + +pub struct TestServer { + pub url: String, + pub api_key: String, + pub secret_key: String, + rooms: Mutex>, + executor: Arc, +} + +impl TestServer { + pub fn create( + url: String, + api_key: String, + secret_key: String, + executor: Arc, + ) -> Result> { + let mut servers = SERVERS.lock(); + if servers.contains_key(&url) { + Err(anyhow!("a server with url {:?} already exists", url)) + } else { + let server = Arc::new(TestServer { + url: url.clone(), + api_key, + secret_key, + rooms: Default::default(), + executor, + }); + servers.insert(url, server.clone()); + Ok(server) + } + } + + fn get(url: &str) -> Result> { + Ok(SERVERS + .lock() + .get(url) + .ok_or_else(|| anyhow!("no server found for url"))? + .clone()) + } + + pub fn teardown(&self) -> Result<()> { + SERVERS + .lock() + .remove(&self.url) + .ok_or_else(|| anyhow!("server with url {:?} does not exist", self.url))?; + Ok(()) + } + + pub fn create_api_client(&self) -> TestApiClient { + TestApiClient { + url: self.url.clone(), + } + } + + pub async fn create_room(&self, room: String) -> Result<()> { + self.executor.simulate_random_delay().await; + let mut server_rooms = self.rooms.lock(); + if server_rooms.contains_key(&room) { + Err(anyhow!("room {:?} already exists", room)) + } else { + server_rooms.insert(room, Default::default()); + Ok(()) + } + } + + async fn delete_room(&self, room: String) -> Result<()> { + // TODO: clear state associated with all `Room`s. + self.executor.simulate_random_delay().await; + let mut server_rooms = self.rooms.lock(); + server_rooms + .remove(&room) + .ok_or_else(|| anyhow!("room {:?} does not exist", room))?; + Ok(()) + } + + async fn join_room(&self, token: String, client_room: Arc) -> Result<()> { + self.executor.simulate_random_delay().await; + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; + let identity = claims.sub.unwrap().to_string(); + let room_name = claims.video.room.unwrap(); + let mut server_rooms = self.rooms.lock(); + let room = (*server_rooms).entry(room_name.to_string()).or_default(); + + if room.client_rooms.contains_key(&identity) { + Err(anyhow!( + "{:?} attempted to join room {:?} twice", + identity, + room_name + )) + } else { + for track in &room.video_tracks { + client_room + .0 + .lock() + .video_track_updates + .0 + .try_broadcast(RemoteVideoTrackUpdate::Subscribed(track.clone())) + .unwrap(); + } + room.client_rooms.insert(identity, client_room); + Ok(()) + } + } + + async fn leave_room(&self, token: String) -> Result<()> { + self.executor.simulate_random_delay().await; + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; + let identity = claims.sub.unwrap().to_string(); + let room_name = claims.video.room.unwrap(); + let mut server_rooms = self.rooms.lock(); + let room = server_rooms + .get_mut(&*room_name) + .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; + room.client_rooms.remove(&identity).ok_or_else(|| { + anyhow!( + "{:?} attempted to leave room {:?} before joining it", + identity, + room_name + ) + })?; + Ok(()) + } + + async fn remove_participant(&self, room_name: String, identity: String) -> Result<()> { + // TODO: clear state associated with the `Room`. + + self.executor.simulate_random_delay().await; + let mut server_rooms = self.rooms.lock(); + let room = server_rooms + .get_mut(&room_name) + .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; + room.client_rooms.remove(&identity).ok_or_else(|| { + anyhow!( + "participant {:?} did not join room {:?}", + identity, + room_name + ) + })?; + Ok(()) + } + + pub async fn disconnect_client(&self, client_identity: String) { + self.executor.simulate_random_delay().await; + let mut server_rooms = self.rooms.lock(); + for room in server_rooms.values_mut() { + if let Some(room) = room.client_rooms.remove(&client_identity) { + *room.0.lock().connection.0.borrow_mut() = ConnectionState::Disconnected; + } + } + } + + async fn publish_video_track(&self, token: String, local_track: LocalVideoTrack) -> Result<()> { + self.executor.simulate_random_delay().await; + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; + let identity = claims.sub.unwrap().to_string(); + let room_name = claims.video.room.unwrap(); + + let mut server_rooms = self.rooms.lock(); + let room = server_rooms + .get_mut(&*room_name) + .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; + + let track = Arc::new(RemoteVideoTrack { + sid: nanoid::nanoid!(17), + publisher_id: identity.clone(), + frames_rx: local_track.frames_rx.clone(), + }); + + room.video_tracks.push(track.clone()); + + for (id, client_room) in &room.client_rooms { + if *id != identity { + let _ = client_room + .0 + .lock() + .video_track_updates + .0 + .try_broadcast(RemoteVideoTrackUpdate::Subscribed(track.clone())) + .unwrap(); + } + } + + Ok(()) + } + + async fn publish_audio_track( + &self, + token: String, + _local_track: &LocalAudioTrack, + ) -> Result<()> { + self.executor.simulate_random_delay().await; + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; + let identity = claims.sub.unwrap().to_string(); + let room_name = claims.video.room.unwrap(); + + let mut server_rooms = self.rooms.lock(); + let room = server_rooms + .get_mut(&*room_name) + .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; + + let track = Arc::new(RemoteAudioTrack { + sid: nanoid::nanoid!(17), + publisher_id: identity.clone(), + }); + + let publication = Arc::new(RemoteTrackPublication); + + room.audio_tracks.push(track.clone()); + + for (id, client_room) in &room.client_rooms { + if *id != identity { + let _ = client_room + .0 + .lock() + .audio_track_updates + .0 + .try_broadcast(RemoteAudioTrackUpdate::Subscribed( + track.clone(), + publication.clone(), + )) + .unwrap(); + } + } + + Ok(()) + } + + fn video_tracks(&self, token: String) -> Result>> { + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; + let room_name = claims.video.room.unwrap(); + + let mut server_rooms = self.rooms.lock(); + let room = server_rooms + .get_mut(&*room_name) + .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; + Ok(room.video_tracks.clone()) + } + + fn audio_tracks(&self, token: String) -> Result>> { + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; + let room_name = claims.video.room.unwrap(); + + let mut server_rooms = self.rooms.lock(); + let room = server_rooms + .get_mut(&*room_name) + .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; + Ok(room.audio_tracks.clone()) + } +} + +#[derive(Default)] +struct TestServerRoom { + client_rooms: HashMap>, + video_tracks: Vec>, + audio_tracks: Vec>, +} + +impl TestServerRoom {} + +pub struct TestApiClient { + url: String, +} + +#[async_trait] +impl live_kit_server::api::Client for TestApiClient { + fn url(&self) -> &str { + &self.url + } + + async fn create_room(&self, name: String) -> Result<()> { + let server = TestServer::get(&self.url)?; + server.create_room(name).await?; + Ok(()) + } + + async fn delete_room(&self, name: String) -> Result<()> { + let server = TestServer::get(&self.url)?; + server.delete_room(name).await?; + Ok(()) + } + + async fn remove_participant(&self, room: String, identity: String) -> Result<()> { + let server = TestServer::get(&self.url)?; + server.remove_participant(room, identity).await?; + Ok(()) + } + + fn room_token(&self, room: &str, identity: &str) -> Result { + let server = TestServer::get(&self.url)?; + token::create( + &server.api_key, + &server.secret_key, + Some(identity), + token::VideoGrant::to_join(room), + ) + } + + fn guest_token(&self, room: &str, identity: &str) -> Result { + let server = TestServer::get(&self.url)?; + token::create( + &server.api_key, + &server.secret_key, + Some(identity), + token::VideoGrant::for_guest(room), + ) + } +} + +pub type Sid = String; + +struct RoomState { + connection: ( + watch::Sender, + watch::Receiver, + ), + display_sources: Vec, + audio_track_updates: ( + async_broadcast::Sender, + async_broadcast::Receiver, + ), + video_track_updates: ( + async_broadcast::Sender, + async_broadcast::Receiver, + ), +} + +#[derive(Clone, Eq, PartialEq)] +pub enum ConnectionState { + Disconnected, + Connected { url: String, token: String }, +} + +pub struct Room(Mutex); + +impl Room { + pub fn new() -> Arc { + Arc::new(Self(Mutex::new(RoomState { + connection: watch::channel_with(ConnectionState::Disconnected), + display_sources: Default::default(), + video_track_updates: async_broadcast::broadcast(128), + audio_track_updates: async_broadcast::broadcast(128), + }))) + } + + pub fn status(&self) -> watch::Receiver { + self.0.lock().connection.1.clone() + } + + pub fn connect(self: &Arc, url: &str, token: &str) -> impl Future> { + let this = self.clone(); + let url = url.to_string(); + let token = token.to_string(); + async move { + let server = TestServer::get(&url)?; + server.join_room(token.clone(), this.clone()).await?; + *this.0.lock().connection.0.borrow_mut() = ConnectionState::Connected { url, token }; + Ok(()) + } + } + + pub fn display_sources(self: &Arc) -> impl Future>> { + let this = self.clone(); + async move { + let server = this.test_server(); + server.executor.simulate_random_delay().await; + Ok(this.0.lock().display_sources.clone()) + } + } + + pub fn publish_video_track( + self: &Arc, + track: LocalVideoTrack, + ) -> impl Future> { + let this = self.clone(); + let track = track.clone(); + async move { + this.test_server() + .publish_video_track(this.token(), track) + .await?; + Ok(LocalTrackPublication) + } + } + pub fn publish_audio_track( + self: &Arc, + track: LocalAudioTrack, + ) -> impl Future> { + let this = self.clone(); + let track = track.clone(); + async move { + this.test_server() + .publish_audio_track(this.token(), &track) + .await?; + Ok(LocalTrackPublication) + } + } + + pub fn unpublish_track(&self, _publication: LocalTrackPublication) {} + + pub fn remote_audio_tracks(&self, publisher_id: &str) -> Vec> { + if !self.is_connected() { + return Vec::new(); + } + + self.test_server() + .audio_tracks(self.token()) + .unwrap() + .into_iter() + .filter(|track| track.publisher_id() == publisher_id) + .collect() + } + + pub fn remote_audio_track_publications( + &self, + publisher_id: &str, + ) -> Vec> { + if !self.is_connected() { + return Vec::new(); + } + + self.test_server() + .audio_tracks(self.token()) + .unwrap() + .into_iter() + .filter(|track| track.publisher_id() == publisher_id) + .map(|_track| Arc::new(RemoteTrackPublication {})) + .collect() + } + + pub fn remote_video_tracks(&self, publisher_id: &str) -> Vec> { + if !self.is_connected() { + return Vec::new(); + } + + self.test_server() + .video_tracks(self.token()) + .unwrap() + .into_iter() + .filter(|track| track.publisher_id() == publisher_id) + .collect() + } + + pub fn remote_audio_track_updates(&self) -> impl Stream { + self.0.lock().audio_track_updates.1.clone() + } + + pub fn remote_video_track_updates(&self) -> impl Stream { + self.0.lock().video_track_updates.1.clone() + } + + pub fn set_display_sources(&self, sources: Vec) { + self.0.lock().display_sources = sources; + } + + fn test_server(&self) -> Arc { + match self.0.lock().connection.1.borrow().clone() { + ConnectionState::Disconnected => panic!("must be connected to call this method"), + ConnectionState::Connected { url, .. } => TestServer::get(&url).unwrap(), + } + } + + fn token(&self) -> String { + match self.0.lock().connection.1.borrow().clone() { + ConnectionState::Disconnected => panic!("must be connected to call this method"), + ConnectionState::Connected { token, .. } => token, + } + } + + fn is_connected(&self) -> bool { + match *self.0.lock().connection.1.borrow() { + ConnectionState::Disconnected => false, + ConnectionState::Connected { .. } => true, + } + } +} + +impl Drop for Room { + fn drop(&mut self) { + if let ConnectionState::Connected { token, .. } = mem::replace( + &mut *self.0.lock().connection.0.borrow_mut(), + ConnectionState::Disconnected, + ) { + if let Ok(server) = TestServer::get(&token) { + let executor = server.executor.clone(); + executor + .spawn(async move { server.leave_room(token).await.unwrap() }) + .detach(); + } + } + } +} + +pub struct LocalTrackPublication; + +impl LocalTrackPublication { + pub fn set_mute(&self, _mute: bool) -> impl Future> { + async { Ok(()) } + } +} + +pub struct RemoteTrackPublication; + +impl RemoteTrackPublication { + pub fn set_enabled(&self, _enabled: bool) -> impl Future> { + async { Ok(()) } + } + + pub fn is_muted(&self) -> bool { + false + } + + pub fn sid(&self) -> String { + "".to_string() + } +} + +#[derive(Clone)] +pub struct LocalVideoTrack { + frames_rx: async_broadcast::Receiver, +} + +impl LocalVideoTrack { + pub fn screen_share_for_display(display: &MacOSDisplay) -> Self { + Self { + frames_rx: display.frames.1.clone(), + } + } +} + +#[derive(Clone)] +pub struct LocalAudioTrack; + +impl LocalAudioTrack { + pub fn create() -> Self { + Self + } +} + +pub struct RemoteVideoTrack { + sid: Sid, + publisher_id: Sid, + frames_rx: async_broadcast::Receiver, +} + +impl RemoteVideoTrack { + pub fn sid(&self) -> &str { + &self.sid + } + + pub fn publisher_id(&self) -> &str { + &self.publisher_id + } + + pub fn frames(&self) -> async_broadcast::Receiver { + self.frames_rx.clone() + } +} + +#[derive(Debug)] +pub struct RemoteAudioTrack { + sid: Sid, + publisher_id: Sid, +} + +impl RemoteAudioTrack { + pub fn sid(&self) -> &str { + &self.sid + } + + pub fn publisher_id(&self) -> &str { + &self.publisher_id + } + + pub fn enable(&self) -> impl Future> { + async { Ok(()) } + } + + pub fn disable(&self) -> impl Future> { + async { Ok(()) } + } +} + +#[derive(Clone)] +pub enum RemoteVideoTrackUpdate { + Subscribed(Arc), + Unsubscribed { publisher_id: Sid, track_id: Sid }, +} + +#[derive(Clone)] +pub enum RemoteAudioTrackUpdate { + ActiveSpeakersChanged { speakers: Vec }, + MuteChanged { track_id: Sid, muted: bool }, + Subscribed(Arc, Arc), + Unsubscribed { publisher_id: Sid, track_id: Sid }, +} + +#[derive(Clone)] +pub struct MacOSDisplay { + frames: ( + async_broadcast::Sender, + async_broadcast::Receiver, + ), +} + +impl MacOSDisplay { + pub fn new() -> Self { + Self { + frames: async_broadcast::broadcast(128), + } + } + + pub fn send_frame(&self, frame: Frame) { + self.frames.0.try_broadcast(frame).unwrap(); + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Frame { + pub label: String, + pub width: usize, + pub height: usize, +} + +impl Frame { + pub fn width(&self) -> usize { + self.width + } + + pub fn height(&self) -> usize { + self.height + } + + pub fn image(&self) -> CVImageBuffer { + unimplemented!("you can't call this in test mode") + } +} From 795369a1e3d08795ad3cdc6a8a41413c90e2e484 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 31 Oct 2023 18:10:23 -0400 Subject: [PATCH 09/18] Port `multi_buffer` to gpui2 --- Cargo.lock | 47 + Cargo.toml | 1 + crates/gpui2/src/subscription.rs | 2 +- crates/multi_buffer2/Cargo.toml | 78 + crates/multi_buffer2/src/anchor.rs | 138 + crates/multi_buffer2/src/multi_buffer2.rs | 5393 +++++++++++++++++++++ 6 files changed, 5658 insertions(+), 1 deletion(-) create mode 100644 crates/multi_buffer2/Cargo.toml create mode 100644 crates/multi_buffer2/src/anchor.rs create mode 100644 crates/multi_buffer2/src/multi_buffer2.rs diff --git a/Cargo.lock b/Cargo.lock index 3aca27106c..7b1a259e25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5035,6 +5035,53 @@ dependencies = [ "workspace", ] +[[package]] +name = "multi_buffer2" +version = "0.1.0" +dependencies = [ + "aho-corasick", + "anyhow", + "client2", + "clock", + "collections", + "convert_case 0.6.0", + "copilot2", + "ctor", + "env_logger 0.9.3", + "futures 0.3.28", + "git", + "gpui2", + "indoc", + "itertools 0.10.5", + "language2", + "lazy_static", + "log", + "lsp2", + "ordered-float 2.10.0", + "parking_lot 0.11.2", + "postage", + "project2", + "pulldown-cmark", + "rand 0.8.5", + "rich_text", + "schemars", + "serde", + "serde_derive", + "settings2", + "smallvec", + "smol", + "snippet", + "sum_tree", + "text", + "theme2", + "tree-sitter", + "tree-sitter-html", + "tree-sitter-rust", + "tree-sitter-typescript", + "unindent", + "util", +] + [[package]] name = "multimap" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index ac490ce935..60742b7416 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ members = [ "crates/menu", "crates/menu2", "crates/multi_buffer", + "crates/multi_buffer2", "crates/node_runtime", "crates/notifications", "crates/outline", diff --git a/crates/gpui2/src/subscription.rs b/crates/gpui2/src/subscription.rs index 3bf28792bb..c2799d2fe6 100644 --- a/crates/gpui2/src/subscription.rs +++ b/crates/gpui2/src/subscription.rs @@ -47,8 +47,8 @@ where subscribers.remove(&subscriber_id); if subscribers.is_empty() { lock.subscribers.remove(&emitter_key); - return; } + return; } // We didn't manage to remove the subscription, which means it was dropped diff --git a/crates/multi_buffer2/Cargo.toml b/crates/multi_buffer2/Cargo.toml new file mode 100644 index 0000000000..4c56bab9dc --- /dev/null +++ b/crates/multi_buffer2/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "multi_buffer2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/multi_buffer2.rs" +doctest = false + +[features] +test-support = [ + "copilot2/test-support", + "text/test-support", + "language2/test-support", + "gpui2/test-support", + "util/test-support", + "tree-sitter-rust", + "tree-sitter-typescript" +] + +[dependencies] +client2 = { path = "../client2" } +clock = { path = "../clock" } +collections = { path = "../collections" } +git = { path = "../git" } +gpui2 = { path = "../gpui2" } +language2 = { path = "../language2" } +lsp2 = { path = "../lsp2" } +rich_text = { path = "../rich_text" } +settings2 = { path = "../settings2" } +snippet = { path = "../snippet" } +sum_tree = { path = "../sum_tree" } +text = { path = "../text" } +theme2 = { path = "../theme2" } +util = { path = "../util" } + +aho-corasick = "1.1" +anyhow.workspace = true +convert_case = "0.6.0" +futures.workspace = true +indoc = "1.0.4" +itertools = "0.10" +lazy_static.workspace = true +log.workspace = true +ordered-float.workspace = true +parking_lot.workspace = true +postage.workspace = true +pulldown-cmark = { version = "0.9.2", default-features = false } +rand.workspace = true +schemars.workspace = true +serde.workspace = true +serde_derive.workspace = true +smallvec.workspace = true +smol.workspace = true + +tree-sitter-rust = { workspace = true, optional = true } +tree-sitter-html = { workspace = true, optional = true } +tree-sitter-typescript = { workspace = true, optional = true } + +[dev-dependencies] +copilot2 = { path = "../copilot2", features = ["test-support"] } +text = { path = "../text", features = ["test-support"] } +language2 = { path = "../language2", features = ["test-support"] } +lsp2 = { path = "../lsp2", features = ["test-support"] } +gpui2 = { path = "../gpui2", features = ["test-support"] } +util = { path = "../util", features = ["test-support"] } +project2 = { path = "../project2", features = ["test-support"] } +settings2 = { path = "../settings2", features = ["test-support"] } + +ctor.workspace = true +env_logger.workspace = true +rand.workspace = true +unindent.workspace = true +tree-sitter.workspace = true +tree-sitter-rust.workspace = true +tree-sitter-html.workspace = true +tree-sitter-typescript.workspace = true diff --git a/crates/multi_buffer2/src/anchor.rs b/crates/multi_buffer2/src/anchor.rs new file mode 100644 index 0000000000..fa65bfc800 --- /dev/null +++ b/crates/multi_buffer2/src/anchor.rs @@ -0,0 +1,138 @@ +use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint}; +use language2::{OffsetUtf16, Point, TextDimension}; +use std::{ + cmp::Ordering, + ops::{Range, Sub}, +}; +use sum_tree::Bias; + +#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] +pub struct Anchor { + pub buffer_id: Option, + pub excerpt_id: ExcerptId, + pub text_anchor: text::Anchor, +} + +impl Anchor { + pub fn min() -> Self { + Self { + buffer_id: None, + excerpt_id: ExcerptId::min(), + text_anchor: text::Anchor::MIN, + } + } + + pub fn max() -> Self { + Self { + buffer_id: None, + excerpt_id: ExcerptId::max(), + text_anchor: text::Anchor::MAX, + } + } + + pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering { + let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot); + if excerpt_id_cmp.is_eq() { + if self.excerpt_id == ExcerptId::min() || self.excerpt_id == ExcerptId::max() { + Ordering::Equal + } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { + self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer) + } else { + Ordering::Equal + } + } else { + excerpt_id_cmp + } + } + + pub fn bias(&self) -> Bias { + self.text_anchor.bias + } + + pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor { + if self.text_anchor.bias != Bias::Left { + if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { + return Self { + buffer_id: self.buffer_id, + excerpt_id: self.excerpt_id.clone(), + text_anchor: self.text_anchor.bias_left(&excerpt.buffer), + }; + } + } + self.clone() + } + + pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor { + if self.text_anchor.bias != Bias::Right { + if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { + return Self { + buffer_id: self.buffer_id, + excerpt_id: self.excerpt_id.clone(), + text_anchor: self.text_anchor.bias_right(&excerpt.buffer), + }; + } + } + self.clone() + } + + pub fn summary(&self, snapshot: &MultiBufferSnapshot) -> D + where + D: TextDimension + Ord + Sub, + { + snapshot.summary_for_anchor(self) + } + + pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool { + if *self == Anchor::min() || *self == Anchor::max() { + true + } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { + excerpt.contains(self) + && (self.text_anchor == excerpt.range.context.start + || self.text_anchor == excerpt.range.context.end + || self.text_anchor.is_valid(&excerpt.buffer)) + } else { + false + } + } +} + +impl ToOffset for Anchor { + fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize { + self.summary(snapshot) + } +} + +impl ToOffsetUtf16 for Anchor { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + self.summary(snapshot) + } +} + +impl ToPoint for Anchor { + fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point { + self.summary(snapshot) + } +} + +pub trait AnchorRangeExt { + fn cmp(&self, b: &Range, buffer: &MultiBufferSnapshot) -> Ordering; + fn to_offset(&self, content: &MultiBufferSnapshot) -> Range; + fn to_point(&self, content: &MultiBufferSnapshot) -> Range; +} + +impl AnchorRangeExt for Range { + fn cmp(&self, other: &Range, buffer: &MultiBufferSnapshot) -> Ordering { + match self.start.cmp(&other.start, buffer) { + Ordering::Equal => other.end.cmp(&self.end, buffer), + ord => ord, + } + } + + fn to_offset(&self, content: &MultiBufferSnapshot) -> Range { + self.start.to_offset(content)..self.end.to_offset(content) + } + + fn to_point(&self, content: &MultiBufferSnapshot) -> Range { + self.start.to_point(content)..self.end.to_point(content) + } +} diff --git a/crates/multi_buffer2/src/multi_buffer2.rs b/crates/multi_buffer2/src/multi_buffer2.rs new file mode 100644 index 0000000000..c5827b8b13 --- /dev/null +++ b/crates/multi_buffer2/src/multi_buffer2.rs @@ -0,0 +1,5393 @@ +mod anchor; + +pub use anchor::{Anchor, AnchorRangeExt}; +use anyhow::{anyhow, Result}; +use clock::ReplicaId; +use collections::{BTreeMap, Bound, HashMap, HashSet}; +use futures::{channel::mpsc, SinkExt}; +use git::diff::DiffHunk; +use gpui2::{AppContext, EventEmitter, Model, ModelContext}; +pub use language2::Completion; +use language2::{ + char_kind, + language_settings::{language_settings, LanguageSettings}, + AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape, + DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, + Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, + ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped, +}; +use std::{ + borrow::Cow, + cell::{Ref, RefCell}, + cmp, fmt, + future::Future, + io, + iter::{self, FromIterator}, + mem, + ops::{Range, RangeBounds, Sub}, + str, + sync::Arc, + time::{Duration, Instant}, +}; +use sum_tree::{Bias, Cursor, SumTree}; +use text::{ + locator::Locator, + subscription::{Subscription, Topic}, + Edit, TextSummary, +}; +use theme2::SyntaxTheme; +use util::post_inc; + +#[cfg(any(test, feature = "test-support"))] +use gpui2::Context; + +const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; + +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ExcerptId(usize); + +pub struct MultiBuffer { + snapshot: RefCell, + buffers: RefCell>, + next_excerpt_id: usize, + subscriptions: Topic, + singleton: bool, + replica_id: ReplicaId, + history: History, + title: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Event { + ExcerptsAdded { + buffer: Model, + predecessor: ExcerptId, + excerpts: Vec<(ExcerptId, ExcerptRange)>, + }, + ExcerptsRemoved { + ids: Vec, + }, + ExcerptsEdited { + ids: Vec, + }, + Edited { + sigleton_buffer_edited: bool, + }, + TransactionUndone { + transaction_id: TransactionId, + }, + Reloaded, + DiffBaseChanged, + LanguageChanged, + Reparsed, + Saved, + FileHandleChanged, + Closed, + DirtyChanged, + DiagnosticsUpdated, +} + +#[derive(Clone)] +struct History { + next_transaction_id: TransactionId, + undo_stack: Vec, + redo_stack: Vec, + transaction_depth: usize, + group_interval: Duration, +} + +#[derive(Clone)] +struct Transaction { + id: TransactionId, + buffer_transactions: HashMap, + first_edit_at: Instant, + last_edit_at: Instant, + suppress_grouping: bool, +} + +pub trait ToOffset: 'static + fmt::Debug { + fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; +} + +pub trait ToOffsetUtf16: 'static + fmt::Debug { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16; +} + +pub trait ToPoint: 'static + fmt::Debug { + fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point; +} + +pub trait ToPointUtf16: 'static + fmt::Debug { + fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16; +} + +struct BufferState { + buffer: Model, + last_version: clock::Global, + last_parse_count: usize, + last_selections_update_count: usize, + last_diagnostics_update_count: usize, + last_file_update_count: usize, + last_git_diff_update_count: usize, + excerpts: Vec, + _subscriptions: [gpui2::Subscription; 2], +} + +#[derive(Clone, Default)] +pub struct MultiBufferSnapshot { + singleton: bool, + excerpts: SumTree, + excerpt_ids: SumTree, + parse_count: usize, + diagnostics_update_count: usize, + trailing_excerpt_update_count: usize, + git_diff_update_count: usize, + edit_count: usize, + is_dirty: bool, + has_conflict: bool, +} + +pub struct ExcerptBoundary { + pub id: ExcerptId, + pub row: u32, + pub buffer: BufferSnapshot, + pub range: ExcerptRange, + pub starts_new_buffer: bool, +} + +#[derive(Clone)] +struct Excerpt { + id: ExcerptId, + locator: Locator, + buffer_id: u64, + buffer: BufferSnapshot, + range: ExcerptRange, + max_buffer_row: u32, + text_summary: TextSummary, + has_trailing_newline: bool, +} + +#[derive(Clone, Debug)] +struct ExcerptIdMapping { + id: ExcerptId, + locator: Locator, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ExcerptRange { + pub context: Range, + pub primary: Option>, +} + +#[derive(Clone, Debug, Default)] +struct ExcerptSummary { + excerpt_id: ExcerptId, + excerpt_locator: Locator, + max_buffer_row: u32, + text: TextSummary, +} + +#[derive(Clone)] +pub struct MultiBufferRows<'a> { + buffer_row_range: Range, + excerpts: Cursor<'a, Excerpt, Point>, +} + +pub struct MultiBufferChunks<'a> { + range: Range, + excerpts: Cursor<'a, Excerpt, usize>, + excerpt_chunks: Option>, + language_aware: bool, +} + +pub struct MultiBufferBytes<'a> { + range: Range, + excerpts: Cursor<'a, Excerpt, usize>, + excerpt_bytes: Option>, + chunk: &'a [u8], +} + +pub struct ReversedMultiBufferBytes<'a> { + range: Range, + excerpts: Cursor<'a, Excerpt, usize>, + excerpt_bytes: Option>, + chunk: &'a [u8], +} + +struct ExcerptChunks<'a> { + content_chunks: BufferChunks<'a>, + footer_height: usize, +} + +struct ExcerptBytes<'a> { + content_bytes: text::Bytes<'a>, + footer_height: usize, +} + +impl MultiBuffer { + pub fn new(replica_id: ReplicaId) -> Self { + Self { + snapshot: Default::default(), + buffers: Default::default(), + next_excerpt_id: 1, + subscriptions: Default::default(), + singleton: false, + replica_id, + history: History { + next_transaction_id: Default::default(), + undo_stack: Default::default(), + redo_stack: Default::default(), + transaction_depth: 0, + group_interval: Duration::from_millis(300), + }, + title: Default::default(), + } + } + + pub fn clone(&self, new_cx: &mut ModelContext) -> Self { + let mut buffers = HashMap::default(); + for (buffer_id, buffer_state) in self.buffers.borrow().iter() { + buffers.insert( + *buffer_id, + BufferState { + buffer: buffer_state.buffer.clone(), + last_version: buffer_state.last_version.clone(), + last_parse_count: buffer_state.last_parse_count, + last_selections_update_count: buffer_state.last_selections_update_count, + last_diagnostics_update_count: buffer_state.last_diagnostics_update_count, + last_file_update_count: buffer_state.last_file_update_count, + last_git_diff_update_count: buffer_state.last_git_diff_update_count, + excerpts: buffer_state.excerpts.clone(), + _subscriptions: [ + new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()), + new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event), + ], + }, + ); + } + Self { + snapshot: RefCell::new(self.snapshot.borrow().clone()), + buffers: RefCell::new(buffers), + next_excerpt_id: 1, + subscriptions: Default::default(), + singleton: self.singleton, + replica_id: self.replica_id, + history: self.history.clone(), + title: self.title.clone(), + } + } + + pub fn with_title(mut self, title: String) -> Self { + self.title = Some(title); + self + } + + pub fn singleton(buffer: Model, cx: &mut ModelContext) -> Self { + let mut this = Self::new(buffer.read(cx).replica_id()); + this.singleton = true; + this.push_excerpts( + buffer, + [ExcerptRange { + context: text::Anchor::MIN..text::Anchor::MAX, + primary: None, + }], + cx, + ); + this.snapshot.borrow_mut().singleton = true; + this + } + + pub fn replica_id(&self) -> ReplicaId { + self.replica_id + } + + pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot { + self.sync(cx); + self.snapshot.borrow().clone() + } + + pub fn read(&self, cx: &AppContext) -> Ref { + self.sync(cx); + self.snapshot.borrow() + } + + pub fn as_singleton(&self) -> Option> { + if self.singleton { + return Some( + self.buffers + .borrow() + .values() + .next() + .unwrap() + .buffer + .clone(), + ); + } else { + None + } + } + + pub fn is_singleton(&self) -> bool { + self.singleton + } + + pub fn subscribe(&mut self) -> Subscription { + self.subscriptions.subscribe() + } + + pub fn is_dirty(&self, cx: &AppContext) -> bool { + self.read(cx).is_dirty() + } + + pub fn has_conflict(&self, cx: &AppContext) -> bool { + self.read(cx).has_conflict() + } + + // The `is_empty` signature doesn't match what clippy expects + #[allow(clippy::len_without_is_empty)] + pub fn len(&self, cx: &AppContext) -> usize { + self.read(cx).len() + } + + pub fn is_empty(&self, cx: &AppContext) -> bool { + self.len(cx) != 0 + } + + pub fn symbols_containing( + &self, + offset: T, + theme: Option<&SyntaxTheme>, + cx: &AppContext, + ) -> Option<(u64, Vec>)> { + self.read(cx).symbols_containing(offset, theme) + } + + pub fn edit( + &mut self, + edits: I, + mut autoindent_mode: Option, + cx: &mut ModelContext, + ) where + I: IntoIterator, T)>, + S: ToOffset, + T: Into>, + { + if self.buffers.borrow().is_empty() { + return; + } + + let snapshot = self.read(cx); + let edits = edits.into_iter().map(|(range, new_text)| { + let mut range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot); + if range.start > range.end { + mem::swap(&mut range.start, &mut range.end); + } + (range, new_text) + }); + + if let Some(buffer) = self.as_singleton() { + return buffer.update(cx, |buffer, cx| { + buffer.edit(edits, autoindent_mode, cx); + }); + } + + let original_indent_columns = match &mut autoindent_mode { + Some(AutoindentMode::Block { + original_indent_columns, + }) => mem::take(original_indent_columns), + _ => Default::default(), + }; + + struct BufferEdit { + range: Range, + new_text: Arc, + is_insertion: bool, + original_indent_column: u32, + } + let mut buffer_edits: HashMap> = Default::default(); + let mut edited_excerpt_ids = Vec::new(); + let mut cursor = snapshot.excerpts.cursor::(); + for (ix, (range, new_text)) in edits.enumerate() { + let new_text: Arc = new_text.into(); + let original_indent_column = original_indent_columns.get(ix).copied().unwrap_or(0); + cursor.seek(&range.start, Bias::Right, &()); + if cursor.item().is_none() && range.start == *cursor.start() { + cursor.prev(&()); + } + let start_excerpt = cursor.item().expect("start offset out of bounds"); + let start_overshoot = range.start - cursor.start(); + let buffer_start = start_excerpt + .range + .context + .start + .to_offset(&start_excerpt.buffer) + + start_overshoot; + edited_excerpt_ids.push(start_excerpt.id); + + cursor.seek(&range.end, Bias::Right, &()); + if cursor.item().is_none() && range.end == *cursor.start() { + cursor.prev(&()); + } + let end_excerpt = cursor.item().expect("end offset out of bounds"); + let end_overshoot = range.end - cursor.start(); + let buffer_end = end_excerpt + .range + .context + .start + .to_offset(&end_excerpt.buffer) + + end_overshoot; + + if start_excerpt.id == end_excerpt.id { + buffer_edits + .entry(start_excerpt.buffer_id) + .or_insert(Vec::new()) + .push(BufferEdit { + range: buffer_start..buffer_end, + new_text, + is_insertion: true, + original_indent_column, + }); + } else { + edited_excerpt_ids.push(end_excerpt.id); + let start_excerpt_range = buffer_start + ..start_excerpt + .range + .context + .end + .to_offset(&start_excerpt.buffer); + let end_excerpt_range = end_excerpt + .range + .context + .start + .to_offset(&end_excerpt.buffer) + ..buffer_end; + buffer_edits + .entry(start_excerpt.buffer_id) + .or_insert(Vec::new()) + .push(BufferEdit { + range: start_excerpt_range, + new_text: new_text.clone(), + is_insertion: true, + original_indent_column, + }); + buffer_edits + .entry(end_excerpt.buffer_id) + .or_insert(Vec::new()) + .push(BufferEdit { + range: end_excerpt_range, + new_text: new_text.clone(), + is_insertion: false, + original_indent_column, + }); + + cursor.seek(&range.start, Bias::Right, &()); + cursor.next(&()); + while let Some(excerpt) = cursor.item() { + if excerpt.id == end_excerpt.id { + break; + } + buffer_edits + .entry(excerpt.buffer_id) + .or_insert(Vec::new()) + .push(BufferEdit { + range: excerpt.range.context.to_offset(&excerpt.buffer), + new_text: new_text.clone(), + is_insertion: false, + original_indent_column, + }); + edited_excerpt_ids.push(excerpt.id); + cursor.next(&()); + } + } + } + + drop(cursor); + drop(snapshot); + // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR. + fn tail( + this: &mut MultiBuffer, + buffer_edits: HashMap>, + autoindent_mode: Option, + edited_excerpt_ids: Vec, + cx: &mut ModelContext, + ) { + for (buffer_id, mut edits) in buffer_edits { + edits.sort_unstable_by_key(|edit| edit.range.start); + this.buffers.borrow()[&buffer_id] + .buffer + .update(cx, |buffer, cx| { + let mut edits = edits.into_iter().peekable(); + let mut insertions = Vec::new(); + let mut original_indent_columns = Vec::new(); + let mut deletions = Vec::new(); + let empty_str: Arc = "".into(); + while let Some(BufferEdit { + mut range, + new_text, + mut is_insertion, + original_indent_column, + }) = edits.next() + { + while let Some(BufferEdit { + range: next_range, + is_insertion: next_is_insertion, + .. + }) = edits.peek() + { + if range.end >= next_range.start { + range.end = cmp::max(next_range.end, range.end); + is_insertion |= *next_is_insertion; + edits.next(); + } else { + break; + } + } + + if is_insertion { + original_indent_columns.push(original_indent_column); + insertions.push(( + buffer.anchor_before(range.start) + ..buffer.anchor_before(range.end), + new_text.clone(), + )); + } else if !range.is_empty() { + deletions.push(( + buffer.anchor_before(range.start) + ..buffer.anchor_before(range.end), + empty_str.clone(), + )); + } + } + + let deletion_autoindent_mode = + if let Some(AutoindentMode::Block { .. }) = autoindent_mode { + Some(AutoindentMode::Block { + original_indent_columns: Default::default(), + }) + } else { + None + }; + let insertion_autoindent_mode = + if let Some(AutoindentMode::Block { .. }) = autoindent_mode { + Some(AutoindentMode::Block { + original_indent_columns, + }) + } else { + None + }; + + buffer.edit(deletions, deletion_autoindent_mode, cx); + buffer.edit(insertions, insertion_autoindent_mode, cx); + }) + } + + cx.emit(Event::ExcerptsEdited { + ids: edited_excerpt_ids, + }); + } + tail(self, buffer_edits, autoindent_mode, edited_excerpt_ids, cx); + } + + pub fn start_transaction(&mut self, cx: &mut ModelContext) -> Option { + self.start_transaction_at(Instant::now(), cx) + } + + pub fn start_transaction_at( + &mut self, + now: Instant, + cx: &mut ModelContext, + ) -> Option { + if let Some(buffer) = self.as_singleton() { + return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); + } + + for BufferState { buffer, .. } in self.buffers.borrow().values() { + buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); + } + self.history.start_transaction(now) + } + + pub fn end_transaction(&mut self, cx: &mut ModelContext) -> Option { + self.end_transaction_at(Instant::now(), cx) + } + + pub fn end_transaction_at( + &mut self, + now: Instant, + cx: &mut ModelContext, + ) -> Option { + if let Some(buffer) = self.as_singleton() { + return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)); + } + + let mut buffer_transactions = HashMap::default(); + for BufferState { buffer, .. } in self.buffers.borrow().values() { + if let Some(transaction_id) = + buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) + { + buffer_transactions.insert(buffer.read(cx).remote_id(), transaction_id); + } + } + + if self.history.end_transaction(now, buffer_transactions) { + let transaction_id = self.history.group().unwrap(); + Some(transaction_id) + } else { + None + } + } + + pub fn merge_transactions( + &mut self, + transaction: TransactionId, + destination: TransactionId, + cx: &mut ModelContext, + ) { + if let Some(buffer) = self.as_singleton() { + buffer.update(cx, |buffer, _| { + buffer.merge_transactions(transaction, destination) + }); + } else { + if let Some(transaction) = self.history.forget(transaction) { + if let Some(destination) = self.history.transaction_mut(destination) { + for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions { + if let Some(destination_buffer_transaction_id) = + destination.buffer_transactions.get(&buffer_id) + { + if let Some(state) = self.buffers.borrow().get(&buffer_id) { + state.buffer.update(cx, |buffer, _| { + buffer.merge_transactions( + buffer_transaction_id, + *destination_buffer_transaction_id, + ) + }); + } + } else { + destination + .buffer_transactions + .insert(buffer_id, buffer_transaction_id); + } + } + } + } + } + } + + pub fn finalize_last_transaction(&mut self, cx: &mut ModelContext) { + self.history.finalize_last_transaction(); + for BufferState { buffer, .. } in self.buffers.borrow().values() { + buffer.update(cx, |buffer, _| { + buffer.finalize_last_transaction(); + }); + } + } + + pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &mut ModelContext) + where + T: IntoIterator, &'a language2::Transaction)>, + { + self.history + .push_transaction(buffer_transactions, Instant::now(), cx); + self.history.finalize_last_transaction(); + } + + pub fn group_until_transaction( + &mut self, + transaction_id: TransactionId, + cx: &mut ModelContext, + ) { + if let Some(buffer) = self.as_singleton() { + buffer.update(cx, |buffer, _| { + buffer.group_until_transaction(transaction_id) + }); + } else { + self.history.group_until(transaction_id); + } + } + + pub fn set_active_selections( + &mut self, + selections: &[Selection], + line_mode: bool, + cursor_shape: CursorShape, + cx: &mut ModelContext, + ) { + let mut selections_by_buffer: HashMap>> = + Default::default(); + let snapshot = self.read(cx); + let mut cursor = snapshot.excerpts.cursor::>(); + for selection in selections { + let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id); + let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id); + + cursor.seek(&Some(start_locator), Bias::Left, &()); + while let Some(excerpt) = cursor.item() { + if excerpt.locator > *end_locator { + break; + } + + let mut start = excerpt.range.context.start; + let mut end = excerpt.range.context.end; + if excerpt.id == selection.start.excerpt_id { + start = selection.start.text_anchor; + } + if excerpt.id == selection.end.excerpt_id { + end = selection.end.text_anchor; + } + selections_by_buffer + .entry(excerpt.buffer_id) + .or_default() + .push(Selection { + id: selection.id, + start, + end, + reversed: selection.reversed, + goal: selection.goal, + }); + + cursor.next(&()); + } + } + + for (buffer_id, buffer_state) in self.buffers.borrow().iter() { + if !selections_by_buffer.contains_key(buffer_id) { + buffer_state + .buffer + .update(cx, |buffer, cx| buffer.remove_active_selections(cx)); + } + } + + for (buffer_id, mut selections) in selections_by_buffer { + self.buffers.borrow()[&buffer_id] + .buffer + .update(cx, |buffer, cx| { + selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer)); + let mut selections = selections.into_iter().peekable(); + let merged_selections = Arc::from_iter(iter::from_fn(|| { + let mut selection = selections.next()?; + while let Some(next_selection) = selections.peek() { + if selection.end.cmp(&next_selection.start, buffer).is_ge() { + let next_selection = selections.next().unwrap(); + if next_selection.end.cmp(&selection.end, buffer).is_ge() { + selection.end = next_selection.end; + } + } else { + break; + } + } + Some(selection) + })); + buffer.set_active_selections(merged_selections, line_mode, cursor_shape, cx); + }); + } + } + + pub fn remove_active_selections(&mut self, cx: &mut ModelContext) { + for buffer in self.buffers.borrow().values() { + buffer + .buffer + .update(cx, |buffer, cx| buffer.remove_active_selections(cx)); + } + } + + pub fn undo(&mut self, cx: &mut ModelContext) -> Option { + let mut transaction_id = None; + if let Some(buffer) = self.as_singleton() { + transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx)); + } else { + while let Some(transaction) = self.history.pop_undo() { + let mut undone = false; + for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions { + if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) { + undone |= buffer.update(cx, |buffer, cx| { + let undo_to = *buffer_transaction_id; + if let Some(entry) = buffer.peek_undo_stack() { + *buffer_transaction_id = entry.transaction_id(); + } + buffer.undo_to_transaction(undo_to, cx) + }); + } + } + + if undone { + transaction_id = Some(transaction.id); + break; + } + } + } + + if let Some(transaction_id) = transaction_id { + cx.emit(Event::TransactionUndone { transaction_id }); + } + + transaction_id + } + + pub fn redo(&mut self, cx: &mut ModelContext) -> Option { + if let Some(buffer) = self.as_singleton() { + return buffer.update(cx, |buffer, cx| buffer.redo(cx)); + } + + while let Some(transaction) = self.history.pop_redo() { + let mut redone = false; + for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions { + if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) { + redone |= buffer.update(cx, |buffer, cx| { + let redo_to = *buffer_transaction_id; + if let Some(entry) = buffer.peek_redo_stack() { + *buffer_transaction_id = entry.transaction_id(); + } + buffer.redo_to_transaction(redo_to, cx) + }); + } + } + + if redone { + return Some(transaction.id); + } + } + + None + } + + pub fn undo_transaction(&mut self, transaction_id: TransactionId, cx: &mut ModelContext) { + if let Some(buffer) = self.as_singleton() { + buffer.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx)); + } else if let Some(transaction) = self.history.remove_from_undo(transaction_id) { + for (buffer_id, transaction_id) in &transaction.buffer_transactions { + if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) { + buffer.update(cx, |buffer, cx| { + buffer.undo_transaction(*transaction_id, cx) + }); + } + } + } + } + + pub fn stream_excerpts_with_context_lines( + &mut self, + buffer: Model, + ranges: Vec>, + context_line_count: u32, + cx: &mut ModelContext, + ) -> mpsc::Receiver> { + let (buffer_id, buffer_snapshot) = + buffer.update(cx, |buffer, _| (buffer.remote_id(), buffer.snapshot())); + + let (mut tx, rx) = mpsc::channel(256); + cx.spawn(move |this, mut cx| async move { + let mut excerpt_ranges = Vec::new(); + let mut range_counts = Vec::new(); + cx.executor() + .scoped(|scope| { + scope.spawn(async { + let (ranges, counts) = + build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count); + excerpt_ranges = ranges; + range_counts = counts; + }); + }) + .await; + + let mut ranges = ranges.into_iter(); + let mut range_counts = range_counts.into_iter(); + for excerpt_ranges in excerpt_ranges.chunks(100) { + let excerpt_ids = match this.update(&mut cx, |this, cx| { + this.push_excerpts(buffer.clone(), excerpt_ranges.iter().cloned(), cx) + }) { + Ok(excerpt_ids) => excerpt_ids, + Err(_) => return, + }; + + for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.by_ref()) + { + for range in ranges.by_ref().take(range_count) { + let start = Anchor { + buffer_id: Some(buffer_id), + excerpt_id: excerpt_id.clone(), + text_anchor: range.start, + }; + let end = Anchor { + buffer_id: Some(buffer_id), + excerpt_id: excerpt_id.clone(), + text_anchor: range.end, + }; + if tx.send(start..end).await.is_err() { + break; + } + } + } + } + }) + .detach(); + + rx + } + + pub fn push_excerpts( + &mut self, + buffer: Model, + ranges: impl IntoIterator>, + cx: &mut ModelContext, + ) -> Vec + where + O: text::ToOffset, + { + self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx) + } + + pub fn push_excerpts_with_context_lines( + &mut self, + buffer: Model, + ranges: Vec>, + context_line_count: u32, + cx: &mut ModelContext, + ) -> Vec> + where + O: text::ToPoint + text::ToOffset, + { + let buffer_id = buffer.read(cx).remote_id(); + let buffer_snapshot = buffer.read(cx).snapshot(); + let (excerpt_ranges, range_counts) = + build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count); + + let excerpt_ids = self.push_excerpts(buffer, excerpt_ranges, cx); + + let mut anchor_ranges = Vec::new(); + let mut ranges = ranges.into_iter(); + for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.into_iter()) { + anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| { + let start = Anchor { + buffer_id: Some(buffer_id), + excerpt_id: excerpt_id.clone(), + text_anchor: buffer_snapshot.anchor_after(range.start), + }; + let end = Anchor { + buffer_id: Some(buffer_id), + excerpt_id: excerpt_id.clone(), + text_anchor: buffer_snapshot.anchor_after(range.end), + }; + start..end + })) + } + anchor_ranges + } + + pub fn insert_excerpts_after( + &mut self, + prev_excerpt_id: ExcerptId, + buffer: Model, + ranges: impl IntoIterator>, + cx: &mut ModelContext, + ) -> Vec + where + O: text::ToOffset, + { + let mut ids = Vec::new(); + let mut next_excerpt_id = self.next_excerpt_id; + self.insert_excerpts_with_ids_after( + prev_excerpt_id, + buffer, + ranges.into_iter().map(|range| { + let id = ExcerptId(post_inc(&mut next_excerpt_id)); + ids.push(id); + (id, range) + }), + cx, + ); + ids + } + + pub fn insert_excerpts_with_ids_after( + &mut self, + prev_excerpt_id: ExcerptId, + buffer: Model, + ranges: impl IntoIterator)>, + cx: &mut ModelContext, + ) where + O: text::ToOffset, + { + assert_eq!(self.history.transaction_depth, 0); + let mut ranges = ranges.into_iter().peekable(); + if ranges.peek().is_none() { + return Default::default(); + } + + self.sync(cx); + + let buffer_id = buffer.read(cx).remote_id(); + let buffer_snapshot = buffer.read(cx).snapshot(); + + let mut buffers = self.buffers.borrow_mut(); + let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState { + last_version: buffer_snapshot.version().clone(), + last_parse_count: buffer_snapshot.parse_count(), + last_selections_update_count: buffer_snapshot.selections_update_count(), + last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(), + last_file_update_count: buffer_snapshot.file_update_count(), + last_git_diff_update_count: buffer_snapshot.git_diff_update_count(), + excerpts: Default::default(), + _subscriptions: [ + cx.observe(&buffer, |_, _, cx| cx.notify()), + cx.subscribe(&buffer, Self::on_buffer_event), + ], + buffer: buffer.clone(), + }); + + let mut snapshot = self.snapshot.borrow_mut(); + + let mut prev_locator = snapshot.excerpt_locator_for_id(prev_excerpt_id).clone(); + let mut new_excerpt_ids = mem::take(&mut snapshot.excerpt_ids); + let mut cursor = snapshot.excerpts.cursor::>(); + let mut new_excerpts = cursor.slice(&prev_locator, Bias::Right, &()); + prev_locator = cursor.start().unwrap_or(Locator::min_ref()).clone(); + + let edit_start = new_excerpts.summary().text.len; + new_excerpts.update_last( + |excerpt| { + excerpt.has_trailing_newline = true; + }, + &(), + ); + + let next_locator = if let Some(excerpt) = cursor.item() { + excerpt.locator.clone() + } else { + Locator::max() + }; + + let mut excerpts = Vec::new(); + while let Some((id, range)) = ranges.next() { + let locator = Locator::between(&prev_locator, &next_locator); + if let Err(ix) = buffer_state.excerpts.binary_search(&locator) { + buffer_state.excerpts.insert(ix, locator.clone()); + } + let range = ExcerptRange { + context: buffer_snapshot.anchor_before(&range.context.start) + ..buffer_snapshot.anchor_after(&range.context.end), + primary: range.primary.map(|primary| { + buffer_snapshot.anchor_before(&primary.start) + ..buffer_snapshot.anchor_after(&primary.end) + }), + }; + if id.0 >= self.next_excerpt_id { + self.next_excerpt_id = id.0 + 1; + } + excerpts.push((id, range.clone())); + let excerpt = Excerpt::new( + id, + locator.clone(), + buffer_id, + buffer_snapshot.clone(), + range, + ranges.peek().is_some() || cursor.item().is_some(), + ); + new_excerpts.push(excerpt, &()); + prev_locator = locator.clone(); + new_excerpt_ids.push(ExcerptIdMapping { id, locator }, &()); + } + + let edit_end = new_excerpts.summary().text.len; + + let suffix = cursor.suffix(&()); + let changed_trailing_excerpt = suffix.is_empty(); + new_excerpts.append(suffix, &()); + drop(cursor); + snapshot.excerpts = new_excerpts; + snapshot.excerpt_ids = new_excerpt_ids; + if changed_trailing_excerpt { + snapshot.trailing_excerpt_update_count += 1; + } + + self.subscriptions.publish_mut([Edit { + old: edit_start..edit_start, + new: edit_start..edit_end, + }]); + cx.emit(Event::Edited { + sigleton_buffer_edited: false, + }); + cx.emit(Event::ExcerptsAdded { + buffer, + predecessor: prev_excerpt_id, + excerpts, + }); + cx.notify(); + } + + pub fn clear(&mut self, cx: &mut ModelContext) { + self.sync(cx); + let ids = self.excerpt_ids(); + self.buffers.borrow_mut().clear(); + let mut snapshot = self.snapshot.borrow_mut(); + let prev_len = snapshot.len(); + snapshot.excerpts = Default::default(); + snapshot.trailing_excerpt_update_count += 1; + snapshot.is_dirty = false; + snapshot.has_conflict = false; + + self.subscriptions.publish_mut([Edit { + old: 0..prev_len, + new: 0..0, + }]); + cx.emit(Event::Edited { + sigleton_buffer_edited: false, + }); + cx.emit(Event::ExcerptsRemoved { ids }); + cx.notify(); + } + + pub fn excerpts_for_buffer( + &self, + buffer: &Model, + cx: &AppContext, + ) -> Vec<(ExcerptId, ExcerptRange)> { + let mut excerpts = Vec::new(); + let snapshot = self.read(cx); + let buffers = self.buffers.borrow(); + let mut cursor = snapshot.excerpts.cursor::>(); + for locator in buffers + .get(&buffer.read(cx).remote_id()) + .map(|state| &state.excerpts) + .into_iter() + .flatten() + { + cursor.seek_forward(&Some(locator), Bias::Left, &()); + if let Some(excerpt) = cursor.item() { + if excerpt.locator == *locator { + excerpts.push((excerpt.id.clone(), excerpt.range.clone())); + } + } + } + + excerpts + } + + pub fn excerpt_ids(&self) -> Vec { + self.snapshot + .borrow() + .excerpts + .iter() + .map(|entry| entry.id) + .collect() + } + + pub fn excerpt_containing( + &self, + position: impl ToOffset, + cx: &AppContext, + ) -> Option<(ExcerptId, Model, Range)> { + let snapshot = self.read(cx); + let position = position.to_offset(&snapshot); + + let mut cursor = snapshot.excerpts.cursor::(); + cursor.seek(&position, Bias::Right, &()); + cursor + .item() + .or_else(|| snapshot.excerpts.last()) + .map(|excerpt| { + ( + excerpt.id.clone(), + self.buffers + .borrow() + .get(&excerpt.buffer_id) + .unwrap() + .buffer + .clone(), + excerpt.range.context.clone(), + ) + }) + } + + // If point is at the end of the buffer, the last excerpt is returned + pub fn point_to_buffer_offset( + &self, + point: T, + cx: &AppContext, + ) -> Option<(Model, usize, ExcerptId)> { + let snapshot = self.read(cx); + let offset = point.to_offset(&snapshot); + let mut cursor = snapshot.excerpts.cursor::(); + cursor.seek(&offset, Bias::Right, &()); + if cursor.item().is_none() { + cursor.prev(&()); + } + + cursor.item().map(|excerpt| { + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let buffer_point = excerpt_start + offset - *cursor.start(); + let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone(); + + (buffer, buffer_point, excerpt.id) + }) + } + + pub fn range_to_buffer_ranges( + &self, + range: Range, + cx: &AppContext, + ) -> Vec<(Model, Range, ExcerptId)> { + let snapshot = self.read(cx); + let start = range.start.to_offset(&snapshot); + let end = range.end.to_offset(&snapshot); + + let mut result = Vec::new(); + let mut cursor = snapshot.excerpts.cursor::(); + cursor.seek(&start, Bias::Right, &()); + if cursor.item().is_none() { + cursor.prev(&()); + } + + while let Some(excerpt) = cursor.item() { + if *cursor.start() > end { + break; + } + + let mut end_before_newline = cursor.end(&()); + if excerpt.has_trailing_newline { + end_before_newline -= 1; + } + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let start = excerpt_start + (cmp::max(start, *cursor.start()) - *cursor.start()); + let end = excerpt_start + (cmp::min(end, end_before_newline) - *cursor.start()); + let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone(); + result.push((buffer, start..end, excerpt.id)); + cursor.next(&()); + } + + result + } + + pub fn remove_excerpts( + &mut self, + excerpt_ids: impl IntoIterator, + cx: &mut ModelContext, + ) { + self.sync(cx); + let ids = excerpt_ids.into_iter().collect::>(); + if ids.is_empty() { + return; + } + + let mut buffers = self.buffers.borrow_mut(); + let mut snapshot = self.snapshot.borrow_mut(); + let mut new_excerpts = SumTree::new(); + let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>(); + let mut edits = Vec::new(); + let mut excerpt_ids = ids.iter().copied().peekable(); + + while let Some(excerpt_id) = excerpt_ids.next() { + // Seek to the next excerpt to remove, preserving any preceding excerpts. + let locator = snapshot.excerpt_locator_for_id(excerpt_id); + new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &()); + + if let Some(mut excerpt) = cursor.item() { + if excerpt.id != excerpt_id { + continue; + } + let mut old_start = cursor.start().1; + + // Skip over the removed excerpt. + 'remove_excerpts: loop { + if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) { + buffer_state.excerpts.retain(|l| l != &excerpt.locator); + if buffer_state.excerpts.is_empty() { + buffers.remove(&excerpt.buffer_id); + } + } + cursor.next(&()); + + // Skip over any subsequent excerpts that are also removed. + while let Some(&next_excerpt_id) = excerpt_ids.peek() { + let next_locator = snapshot.excerpt_locator_for_id(next_excerpt_id); + if let Some(next_excerpt) = cursor.item() { + if next_excerpt.locator == *next_locator { + excerpt_ids.next(); + excerpt = next_excerpt; + continue 'remove_excerpts; + } + } + break; + } + + break; + } + + // When removing the last excerpt, remove the trailing newline from + // the previous excerpt. + if cursor.item().is_none() && old_start > 0 { + old_start -= 1; + new_excerpts.update_last(|e| e.has_trailing_newline = false, &()); + } + + // Push an edit for the removal of this run of excerpts. + let old_end = cursor.start().1; + let new_start = new_excerpts.summary().text.len; + edits.push(Edit { + old: old_start..old_end, + new: new_start..new_start, + }); + } + } + let suffix = cursor.suffix(&()); + let changed_trailing_excerpt = suffix.is_empty(); + new_excerpts.append(suffix, &()); + drop(cursor); + snapshot.excerpts = new_excerpts; + + if changed_trailing_excerpt { + snapshot.trailing_excerpt_update_count += 1; + } + + self.subscriptions.publish_mut(edits); + cx.emit(Event::Edited { + sigleton_buffer_edited: false, + }); + cx.emit(Event::ExcerptsRemoved { ids }); + cx.notify(); + } + + pub fn wait_for_anchors<'a>( + &self, + anchors: impl 'a + Iterator, + cx: &mut ModelContext, + ) -> impl 'static + Future> { + let borrow = self.buffers.borrow(); + let mut error = None; + let mut futures = Vec::new(); + for anchor in anchors { + if let Some(buffer_id) = anchor.buffer_id { + if let Some(buffer) = borrow.get(&buffer_id) { + buffer.buffer.update(cx, |buffer, _| { + futures.push(buffer.wait_for_anchors([anchor.text_anchor])) + }); + } else { + error = Some(anyhow!( + "buffer {buffer_id} is not part of this multi-buffer" + )); + break; + } + } + } + async move { + if let Some(error) = error { + Err(error)?; + } + for future in futures { + future.await?; + } + Ok(()) + } + } + + pub fn text_anchor_for_position( + &self, + position: T, + cx: &AppContext, + ) -> Option<(Model, language2::Anchor)> { + let snapshot = self.read(cx); + let anchor = snapshot.anchor_before(position); + let buffer = self + .buffers + .borrow() + .get(&anchor.buffer_id?)? + .buffer + .clone(); + Some((buffer, anchor.text_anchor)) + } + + fn on_buffer_event( + &mut self, + _: Model, + event: &language2::Event, + cx: &mut ModelContext, + ) { + cx.emit(match event { + language2::Event::Edited => Event::Edited { + sigleton_buffer_edited: true, + }, + language2::Event::DirtyChanged => Event::DirtyChanged, + language2::Event::Saved => Event::Saved, + language2::Event::FileHandleChanged => Event::FileHandleChanged, + language2::Event::Reloaded => Event::Reloaded, + language2::Event::DiffBaseChanged => Event::DiffBaseChanged, + language2::Event::LanguageChanged => Event::LanguageChanged, + language2::Event::Reparsed => Event::Reparsed, + language2::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated, + language2::Event::Closed => Event::Closed, + + // + language2::Event::Operation(_) => return, + }); + } + + pub fn all_buffers(&self) -> HashSet> { + self.buffers + .borrow() + .values() + .map(|state| state.buffer.clone()) + .collect() + } + + pub fn buffer(&self, buffer_id: u64) -> Option> { + self.buffers + .borrow() + .get(&buffer_id) + .map(|state| state.buffer.clone()) + } + + pub fn is_completion_trigger(&self, position: Anchor, text: &str, cx: &AppContext) -> bool { + let mut chars = text.chars(); + let char = if let Some(char) = chars.next() { + char + } else { + return false; + }; + if chars.next().is_some() { + return false; + } + + let snapshot = self.snapshot(cx); + let position = position.to_offset(&snapshot); + let scope = snapshot.language_scope_at(position); + if char_kind(&scope, char) == CharKind::Word { + return true; + } + + let anchor = snapshot.anchor_before(position); + anchor + .buffer_id + .and_then(|buffer_id| { + let buffer = self.buffers.borrow().get(&buffer_id)?.buffer.clone(); + Some( + buffer + .read(cx) + .completion_triggers() + .iter() + .any(|string| string == text), + ) + }) + .unwrap_or(false) + } + + pub fn language_at<'a, T: ToOffset>( + &self, + point: T, + cx: &'a AppContext, + ) -> Option> { + self.point_to_buffer_offset(point, cx) + .and_then(|(buffer, offset, _)| buffer.read(cx).language_at(offset)) + } + + pub fn settings_at<'a, T: ToOffset>( + &self, + point: T, + cx: &'a AppContext, + ) -> &'a LanguageSettings { + let mut language = None; + let mut file = None; + if let Some((buffer, offset, _)) = self.point_to_buffer_offset(point, cx) { + let buffer = buffer.read(cx); + language = buffer.language_at(offset); + file = buffer.file(); + } + language_settings(language.as_ref(), file, cx) + } + + pub fn for_each_buffer(&self, mut f: impl FnMut(&Model)) { + self.buffers + .borrow() + .values() + .for_each(|state| f(&state.buffer)) + } + + pub fn title<'a>(&'a self, cx: &'a AppContext) -> Cow<'a, str> { + if let Some(title) = self.title.as_ref() { + return title.into(); + } + + if let Some(buffer) = self.as_singleton() { + if let Some(file) = buffer.read(cx).file() { + return file.file_name(cx).to_string_lossy(); + } + } + + "untitled".into() + } + + #[cfg(any(test, feature = "test-support"))] + pub fn is_parsing(&self, cx: &AppContext) -> bool { + self.as_singleton().unwrap().read(cx).is_parsing() + } + + fn sync(&self, cx: &AppContext) { + let mut snapshot = self.snapshot.borrow_mut(); + let mut excerpts_to_edit = Vec::new(); + let mut reparsed = false; + let mut diagnostics_updated = false; + let mut git_diff_updated = false; + let mut is_dirty = false; + let mut has_conflict = false; + let mut edited = false; + let mut buffers = self.buffers.borrow_mut(); + for buffer_state in buffers.values_mut() { + let buffer = buffer_state.buffer.read(cx); + let version = buffer.version(); + let parse_count = buffer.parse_count(); + let selections_update_count = buffer.selections_update_count(); + let diagnostics_update_count = buffer.diagnostics_update_count(); + let file_update_count = buffer.file_update_count(); + let git_diff_update_count = buffer.git_diff_update_count(); + + let buffer_edited = version.changed_since(&buffer_state.last_version); + let buffer_reparsed = parse_count > buffer_state.last_parse_count; + let buffer_selections_updated = + selections_update_count > buffer_state.last_selections_update_count; + let buffer_diagnostics_updated = + diagnostics_update_count > buffer_state.last_diagnostics_update_count; + let buffer_file_updated = file_update_count > buffer_state.last_file_update_count; + let buffer_git_diff_updated = + git_diff_update_count > buffer_state.last_git_diff_update_count; + if buffer_edited + || buffer_reparsed + || buffer_selections_updated + || buffer_diagnostics_updated + || buffer_file_updated + || buffer_git_diff_updated + { + buffer_state.last_version = version; + buffer_state.last_parse_count = parse_count; + buffer_state.last_selections_update_count = selections_update_count; + buffer_state.last_diagnostics_update_count = diagnostics_update_count; + buffer_state.last_file_update_count = file_update_count; + buffer_state.last_git_diff_update_count = git_diff_update_count; + excerpts_to_edit.extend( + buffer_state + .excerpts + .iter() + .map(|locator| (locator, buffer_state.buffer.clone(), buffer_edited)), + ); + } + + edited |= buffer_edited; + reparsed |= buffer_reparsed; + diagnostics_updated |= buffer_diagnostics_updated; + git_diff_updated |= buffer_git_diff_updated; + is_dirty |= buffer.is_dirty(); + has_conflict |= buffer.has_conflict(); + } + if edited { + snapshot.edit_count += 1; + } + if reparsed { + snapshot.parse_count += 1; + } + if diagnostics_updated { + snapshot.diagnostics_update_count += 1; + } + if git_diff_updated { + snapshot.git_diff_update_count += 1; + } + snapshot.is_dirty = is_dirty; + snapshot.has_conflict = has_conflict; + + excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator); + + let mut edits = Vec::new(); + let mut new_excerpts = SumTree::new(); + let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>(); + + for (locator, buffer, buffer_edited) in excerpts_to_edit { + new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &()); + let old_excerpt = cursor.item().unwrap(); + let buffer = buffer.read(cx); + let buffer_id = buffer.remote_id(); + + let mut new_excerpt; + if buffer_edited { + edits.extend( + buffer + .edits_since_in_range::( + old_excerpt.buffer.version(), + old_excerpt.range.context.clone(), + ) + .map(|mut edit| { + let excerpt_old_start = cursor.start().1; + let excerpt_new_start = new_excerpts.summary().text.len; + edit.old.start += excerpt_old_start; + edit.old.end += excerpt_old_start; + edit.new.start += excerpt_new_start; + edit.new.end += excerpt_new_start; + edit + }), + ); + + new_excerpt = Excerpt::new( + old_excerpt.id, + locator.clone(), + buffer_id, + buffer.snapshot(), + old_excerpt.range.clone(), + old_excerpt.has_trailing_newline, + ); + } else { + new_excerpt = old_excerpt.clone(); + new_excerpt.buffer = buffer.snapshot(); + } + + new_excerpts.push(new_excerpt, &()); + cursor.next(&()); + } + new_excerpts.append(cursor.suffix(&()), &()); + + drop(cursor); + snapshot.excerpts = new_excerpts; + + self.subscriptions.publish(edits); + } +} + +#[cfg(any(test, feature = "test-support"))] +impl MultiBuffer { + pub fn build_simple(text: &str, cx: &mut gpui2::AppContext) -> Model { + let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + cx.build_model(|cx| Self::singleton(buffer, cx)) + } + + pub fn build_multi( + excerpts: [(&str, Vec>); COUNT], + cx: &mut gpui2::AppContext, + ) -> Model { + let multi = cx.build_model(|_| Self::new(0)); + for (text, ranges) in excerpts { + let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange { + context: range, + primary: None, + }); + multi.update(cx, |multi, cx| { + multi.push_excerpts(buffer, excerpt_ranges, cx) + }); + } + + multi + } + + pub fn build_from_buffer(buffer: Model, cx: &mut gpui2::AppContext) -> Model { + cx.build_model(|cx| Self::singleton(buffer, cx)) + } + + pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui2::AppContext) -> Model { + cx.build_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + let mutation_count = rng.gen_range(1..=5); + multibuffer.randomly_edit_excerpts(rng, mutation_count, cx); + multibuffer + }) + } + + pub fn randomly_edit( + &mut self, + rng: &mut impl rand::Rng, + edit_count: usize, + cx: &mut ModelContext, + ) { + use util::RandomCharIter; + + let snapshot = self.read(cx); + let mut edits: Vec<(Range, Arc)> = Vec::new(); + let mut last_end = None; + for _ in 0..edit_count { + if last_end.map_or(false, |last_end| last_end >= snapshot.len()) { + break; + } + + let new_start = last_end.map_or(0, |last_end| last_end + 1); + let end = snapshot.clip_offset(rng.gen_range(new_start..=snapshot.len()), Bias::Right); + let start = snapshot.clip_offset(rng.gen_range(new_start..=end), Bias::Right); + last_end = Some(end); + + let mut range = start..end; + if rng.gen_bool(0.2) { + mem::swap(&mut range.start, &mut range.end); + } + + let new_text_len = rng.gen_range(0..10); + let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); + + edits.push((range, new_text.into())); + } + log::info!("mutating multi-buffer with {:?}", edits); + drop(snapshot); + + self.edit(edits, None, cx); + } + + pub fn randomly_edit_excerpts( + &mut self, + rng: &mut impl rand::Rng, + mutation_count: usize, + cx: &mut ModelContext, + ) { + use rand::prelude::*; + use std::env; + use util::RandomCharIter; + + let max_excerpts = env::var("MAX_EXCERPTS") + .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable")) + .unwrap_or(5); + + let mut buffers = Vec::new(); + for _ in 0..mutation_count { + if rng.gen_bool(0.05) { + log::info!("Clearing multi-buffer"); + self.clear(cx); + continue; + } + + let excerpt_ids = self.excerpt_ids(); + if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) { + let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() { + let text = RandomCharIter::new(&mut *rng).take(10).collect::(); + buffers + .push(cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text))); + let buffer = buffers.last().unwrap().read(cx); + log::info!( + "Creating new buffer {} with text: {:?}", + buffer.remote_id(), + buffer.text() + ); + buffers.last().unwrap().clone() + } else { + self.buffers + .borrow() + .values() + .choose(rng) + .unwrap() + .buffer + .clone() + }; + + let buffer = buffer_handle.read(cx); + let buffer_text = buffer.text(); + let ranges = (0..rng.gen_range(0..5)) + .map(|_| { + let end_ix = + buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right); + let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); + ExcerptRange { + context: start_ix..end_ix, + primary: None, + } + }) + .collect::>(); + log::info!( + "Inserting excerpts from buffer {} and ranges {:?}: {:?}", + buffer_handle.read(cx).remote_id(), + ranges.iter().map(|r| &r.context).collect::>(), + ranges + .iter() + .map(|r| &buffer_text[r.context.clone()]) + .collect::>() + ); + + let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx); + log::info!("Inserted with ids: {:?}", excerpt_id); + } else { + let remove_count = rng.gen_range(1..=excerpt_ids.len()); + let mut excerpts_to_remove = excerpt_ids + .choose_multiple(rng, remove_count) + .cloned() + .collect::>(); + let snapshot = self.snapshot.borrow(); + excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &*snapshot)); + drop(snapshot); + log::info!("Removing excerpts {:?}", excerpts_to_remove); + self.remove_excerpts(excerpts_to_remove, cx); + } + } + } + + pub fn randomly_mutate( + &mut self, + rng: &mut impl rand::Rng, + mutation_count: usize, + cx: &mut ModelContext, + ) { + use rand::prelude::*; + + if rng.gen_bool(0.7) || self.singleton { + let buffer = self + .buffers + .borrow() + .values() + .choose(rng) + .map(|state| state.buffer.clone()); + + if let Some(buffer) = buffer { + buffer.update(cx, |buffer, cx| { + if rng.gen() { + buffer.randomly_edit(rng, mutation_count, cx); + } else { + buffer.randomly_undo_redo(rng, cx); + } + }); + } else { + self.randomly_edit(rng, mutation_count, cx); + } + } else { + self.randomly_edit_excerpts(rng, mutation_count, cx); + } + + self.check_invariants(cx); + } + + fn check_invariants(&self, cx: &mut ModelContext) { + let snapshot = self.read(cx); + let excerpts = snapshot.excerpts.items(&()); + let excerpt_ids = snapshot.excerpt_ids.items(&()); + + for (ix, excerpt) in excerpts.iter().enumerate() { + if ix == 0 { + if excerpt.locator <= Locator::min() { + panic!("invalid first excerpt locator {:?}", excerpt.locator); + } + } else { + if excerpt.locator <= excerpts[ix - 1].locator { + panic!("excerpts are out-of-order: {:?}", excerpts); + } + } + } + + for (ix, entry) in excerpt_ids.iter().enumerate() { + if ix == 0 { + if entry.id.cmp(&ExcerptId::min(), &*snapshot).is_le() { + panic!("invalid first excerpt id {:?}", entry.id); + } + } else { + if entry.id <= excerpt_ids[ix - 1].id { + panic!("excerpt ids are out-of-order: {:?}", excerpt_ids); + } + } + } + } +} + +impl EventEmitter for MultiBuffer { + type Event = Event; +} + +impl MultiBufferSnapshot { + pub fn text(&self) -> String { + self.chunks(0..self.len(), false) + .map(|chunk| chunk.text) + .collect() + } + + pub fn reversed_chars_at(&self, position: T) -> impl Iterator + '_ { + let mut offset = position.to_offset(self); + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&offset, Bias::Left, &()); + let mut excerpt_chunks = cursor.item().map(|excerpt| { + let end_before_footer = cursor.start() + excerpt.text_summary.len; + let start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let end = start + (cmp::min(offset, end_before_footer) - cursor.start()); + excerpt.buffer.reversed_chunks_in_range(start..end) + }); + iter::from_fn(move || { + if offset == *cursor.start() { + cursor.prev(&()); + let excerpt = cursor.item()?; + excerpt_chunks = Some( + excerpt + .buffer + .reversed_chunks_in_range(excerpt.range.context.clone()), + ); + } + + let excerpt = cursor.item().unwrap(); + if offset == cursor.end(&()) && excerpt.has_trailing_newline { + offset -= 1; + Some("\n") + } else { + let chunk = excerpt_chunks.as_mut().unwrap().next().unwrap(); + offset -= chunk.len(); + Some(chunk) + } + }) + .flat_map(|c| c.chars().rev()) + } + + pub fn chars_at(&self, position: T) -> impl Iterator + '_ { + let offset = position.to_offset(self); + self.text_for_range(offset..self.len()) + .flat_map(|chunk| chunk.chars()) + } + + pub fn text_for_range(&self, range: Range) -> impl Iterator + '_ { + self.chunks(range, false).map(|chunk| chunk.text) + } + + pub fn is_line_blank(&self, row: u32) -> bool { + self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row))) + .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none()) + } + + pub fn contains_str_at(&self, position: T, needle: &str) -> bool + where + T: ToOffset, + { + let position = position.to_offset(self); + position == self.clip_offset(position, Bias::Left) + && self + .bytes_in_range(position..self.len()) + .flatten() + .copied() + .take(needle.len()) + .eq(needle.bytes()) + } + + pub fn surrounding_word(&self, start: T) -> (Range, Option) { + let mut start = start.to_offset(self); + let mut end = start; + let mut next_chars = self.chars_at(start).peekable(); + let mut prev_chars = self.reversed_chars_at(start).peekable(); + + let scope = self.language_scope_at(start); + let kind = |c| char_kind(&scope, c); + let word_kind = cmp::max( + prev_chars.peek().copied().map(kind), + next_chars.peek().copied().map(kind), + ); + + for ch in prev_chars { + if Some(kind(ch)) == word_kind && ch != '\n' { + start -= ch.len_utf8(); + } else { + break; + } + } + + for ch in next_chars { + if Some(kind(ch)) == word_kind && ch != '\n' { + end += ch.len_utf8(); + } else { + break; + } + } + + (start..end, word_kind) + } + + pub fn as_singleton(&self) -> Option<(&ExcerptId, u64, &BufferSnapshot)> { + if self.singleton { + self.excerpts + .iter() + .next() + .map(|e| (&e.id, e.buffer_id, &e.buffer)) + } else { + None + } + } + + pub fn len(&self) -> usize { + self.excerpts.summary().text.len + } + + pub fn is_empty(&self) -> bool { + self.excerpts.summary().text.len == 0 + } + + pub fn max_buffer_row(&self) -> u32 { + self.excerpts.summary().max_buffer_row + } + + pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.clip_offset(offset, bias); + } + + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&offset, Bias::Right, &()); + let overshoot = if let Some(excerpt) = cursor.item() { + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let buffer_offset = excerpt + .buffer + .clip_offset(excerpt_start + (offset - cursor.start()), bias); + buffer_offset.saturating_sub(excerpt_start) + } else { + 0 + }; + cursor.start() + overshoot + } + + pub fn clip_point(&self, point: Point, bias: Bias) -> Point { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.clip_point(point, bias); + } + + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&point, Bias::Right, &()); + let overshoot = if let Some(excerpt) = cursor.item() { + let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer); + let buffer_point = excerpt + .buffer + .clip_point(excerpt_start + (point - cursor.start()), bias); + buffer_point.saturating_sub(excerpt_start) + } else { + Point::zero() + }; + *cursor.start() + overshoot + } + + pub fn clip_offset_utf16(&self, offset: OffsetUtf16, bias: Bias) -> OffsetUtf16 { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.clip_offset_utf16(offset, bias); + } + + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&offset, Bias::Right, &()); + let overshoot = if let Some(excerpt) = cursor.item() { + let excerpt_start = excerpt.range.context.start.to_offset_utf16(&excerpt.buffer); + let buffer_offset = excerpt + .buffer + .clip_offset_utf16(excerpt_start + (offset - cursor.start()), bias); + OffsetUtf16(buffer_offset.0.saturating_sub(excerpt_start.0)) + } else { + OffsetUtf16(0) + }; + *cursor.start() + overshoot + } + + pub fn clip_point_utf16(&self, point: Unclipped, bias: Bias) -> PointUtf16 { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.clip_point_utf16(point, bias); + } + + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&point.0, Bias::Right, &()); + let overshoot = if let Some(excerpt) = cursor.item() { + let excerpt_start = excerpt + .buffer + .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); + let buffer_point = excerpt + .buffer + .clip_point_utf16(Unclipped(excerpt_start + (point.0 - cursor.start())), bias); + buffer_point.saturating_sub(excerpt_start) + } else { + PointUtf16::zero() + }; + *cursor.start() + overshoot + } + + pub fn bytes_in_range(&self, range: Range) -> MultiBufferBytes { + let range = range.start.to_offset(self)..range.end.to_offset(self); + let mut excerpts = self.excerpts.cursor::(); + excerpts.seek(&range.start, Bias::Right, &()); + + let mut chunk = &[][..]; + let excerpt_bytes = if let Some(excerpt) = excerpts.item() { + let mut excerpt_bytes = excerpt + .bytes_in_range(range.start - excerpts.start()..range.end - excerpts.start()); + chunk = excerpt_bytes.next().unwrap_or(&[][..]); + Some(excerpt_bytes) + } else { + None + }; + MultiBufferBytes { + range, + excerpts, + excerpt_bytes, + chunk, + } + } + + pub fn reversed_bytes_in_range( + &self, + range: Range, + ) -> ReversedMultiBufferBytes { + let range = range.start.to_offset(self)..range.end.to_offset(self); + let mut excerpts = self.excerpts.cursor::(); + excerpts.seek(&range.end, Bias::Left, &()); + + let mut chunk = &[][..]; + let excerpt_bytes = if let Some(excerpt) = excerpts.item() { + let mut excerpt_bytes = excerpt.reversed_bytes_in_range( + range.start - excerpts.start()..range.end - excerpts.start(), + ); + chunk = excerpt_bytes.next().unwrap_or(&[][..]); + Some(excerpt_bytes) + } else { + None + }; + + ReversedMultiBufferBytes { + range, + excerpts, + excerpt_bytes, + chunk, + } + } + + pub fn buffer_rows(&self, start_row: u32) -> MultiBufferRows { + let mut result = MultiBufferRows { + buffer_row_range: 0..0, + excerpts: self.excerpts.cursor(), + }; + result.seek(start_row); + result + } + + pub fn chunks(&self, range: Range, language_aware: bool) -> MultiBufferChunks { + let range = range.start.to_offset(self)..range.end.to_offset(self); + let mut chunks = MultiBufferChunks { + range: range.clone(), + excerpts: self.excerpts.cursor(), + excerpt_chunks: None, + language_aware, + }; + chunks.seek(range.start); + chunks + } + + pub fn offset_to_point(&self, offset: usize) -> Point { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.offset_to_point(offset); + } + + let mut cursor = self.excerpts.cursor::<(usize, Point)>(); + cursor.seek(&offset, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset, start_point) = cursor.start(); + let overshoot = offset - start_offset; + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer); + let buffer_point = excerpt + .buffer + .offset_to_point(excerpt_start_offset + overshoot); + *start_point + (buffer_point - excerpt_start_point) + } else { + self.excerpts.summary().text.lines + } + } + + pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.offset_to_point_utf16(offset); + } + + let mut cursor = self.excerpts.cursor::<(usize, PointUtf16)>(); + cursor.seek(&offset, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset, start_point) = cursor.start(); + let overshoot = offset - start_offset; + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point_utf16(&excerpt.buffer); + let buffer_point = excerpt + .buffer + .offset_to_point_utf16(excerpt_start_offset + overshoot); + *start_point + (buffer_point - excerpt_start_point) + } else { + self.excerpts.summary().text.lines_utf16() + } + } + + pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.point_to_point_utf16(point); + } + + let mut cursor = self.excerpts.cursor::<(Point, PointUtf16)>(); + cursor.seek(&point, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset, start_point) = cursor.start(); + let overshoot = point - start_offset; + let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer); + let excerpt_start_point_utf16 = + excerpt.range.context.start.to_point_utf16(&excerpt.buffer); + let buffer_point = excerpt + .buffer + .point_to_point_utf16(excerpt_start_point + overshoot); + *start_point + (buffer_point - excerpt_start_point_utf16) + } else { + self.excerpts.summary().text.lines_utf16() + } + } + + pub fn point_to_offset(&self, point: Point) -> usize { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.point_to_offset(point); + } + + let mut cursor = self.excerpts.cursor::<(Point, usize)>(); + cursor.seek(&point, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_point, start_offset) = cursor.start(); + let overshoot = point - start_point; + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer); + let buffer_offset = excerpt + .buffer + .point_to_offset(excerpt_start_point + overshoot); + *start_offset + buffer_offset - excerpt_start_offset + } else { + self.excerpts.summary().text.len + } + } + + pub fn offset_utf16_to_offset(&self, offset_utf16: OffsetUtf16) -> usize { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.offset_utf16_to_offset(offset_utf16); + } + + let mut cursor = self.excerpts.cursor::<(OffsetUtf16, usize)>(); + cursor.seek(&offset_utf16, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset_utf16, start_offset) = cursor.start(); + let overshoot = offset_utf16 - start_offset_utf16; + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_offset_utf16 = + excerpt.buffer.offset_to_offset_utf16(excerpt_start_offset); + let buffer_offset = excerpt + .buffer + .offset_utf16_to_offset(excerpt_start_offset_utf16 + overshoot); + *start_offset + (buffer_offset - excerpt_start_offset) + } else { + self.excerpts.summary().text.len + } + } + + pub fn offset_to_offset_utf16(&self, offset: usize) -> OffsetUtf16 { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.offset_to_offset_utf16(offset); + } + + let mut cursor = self.excerpts.cursor::<(usize, OffsetUtf16)>(); + cursor.seek(&offset, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset, start_offset_utf16) = cursor.start(); + let overshoot = offset - start_offset; + let excerpt_start_offset_utf16 = + excerpt.range.context.start.to_offset_utf16(&excerpt.buffer); + let excerpt_start_offset = excerpt + .buffer + .offset_utf16_to_offset(excerpt_start_offset_utf16); + let buffer_offset_utf16 = excerpt + .buffer + .offset_to_offset_utf16(excerpt_start_offset + overshoot); + *start_offset_utf16 + (buffer_offset_utf16 - excerpt_start_offset_utf16) + } else { + self.excerpts.summary().text.len_utf16 + } + } + + pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer.point_utf16_to_offset(point); + } + + let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>(); + cursor.seek(&point, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_point, start_offset) = cursor.start(); + let overshoot = point - start_point; + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt + .buffer + .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); + let buffer_offset = excerpt + .buffer + .point_utf16_to_offset(excerpt_start_point + overshoot); + *start_offset + (buffer_offset - excerpt_start_offset) + } else { + self.excerpts.summary().text.len + } + } + + pub fn point_to_buffer_offset( + &self, + point: T, + ) -> Option<(&BufferSnapshot, usize)> { + let offset = point.to_offset(&self); + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&offset, Bias::Right, &()); + if cursor.item().is_none() { + cursor.prev(&()); + } + + cursor.item().map(|excerpt| { + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let buffer_point = excerpt_start + offset - *cursor.start(); + (&excerpt.buffer, buffer_point) + }) + } + + pub fn suggested_indents( + &self, + rows: impl IntoIterator, + cx: &AppContext, + ) -> BTreeMap { + let mut result = BTreeMap::new(); + + let mut rows_for_excerpt = Vec::new(); + let mut cursor = self.excerpts.cursor::(); + let mut rows = rows.into_iter().peekable(); + let mut prev_row = u32::MAX; + let mut prev_language_indent_size = IndentSize::default(); + + while let Some(row) = rows.next() { + cursor.seek(&Point::new(row, 0), Bias::Right, &()); + let excerpt = match cursor.item() { + Some(excerpt) => excerpt, + _ => continue, + }; + + // Retrieve the language and indent size once for each disjoint region being indented. + let single_indent_size = if row.saturating_sub(1) == prev_row { + prev_language_indent_size + } else { + excerpt + .buffer + .language_indent_size_at(Point::new(row, 0), cx) + }; + prev_language_indent_size = single_indent_size; + prev_row = row; + + let start_buffer_row = excerpt.range.context.start.to_point(&excerpt.buffer).row; + let start_multibuffer_row = cursor.start().row; + + rows_for_excerpt.push(row); + while let Some(next_row) = rows.peek().copied() { + if cursor.end(&()).row > next_row { + rows_for_excerpt.push(next_row); + rows.next(); + } else { + break; + } + } + + let buffer_rows = rows_for_excerpt + .drain(..) + .map(|row| start_buffer_row + row - start_multibuffer_row); + let buffer_indents = excerpt + .buffer + .suggested_indents(buffer_rows, single_indent_size); + let multibuffer_indents = buffer_indents + .into_iter() + .map(|(row, indent)| (start_multibuffer_row + row - start_buffer_row, indent)); + result.extend(multibuffer_indents); + } + + result + } + + pub fn indent_size_for_line(&self, row: u32) -> IndentSize { + if let Some((buffer, range)) = self.buffer_line_for_row(row) { + let mut size = buffer.indent_size_for_line(range.start.row); + size.len = size + .len + .min(range.end.column) + .saturating_sub(range.start.column); + size + } else { + IndentSize::spaces(0) + } + } + + pub fn prev_non_blank_row(&self, mut row: u32) -> Option { + while row > 0 { + row -= 1; + if !self.is_line_blank(row) { + return Some(row); + } + } + None + } + + pub fn line_len(&self, row: u32) -> u32 { + if let Some((_, range)) = self.buffer_line_for_row(row) { + range.end.column - range.start.column + } else { + 0 + } + } + + pub fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range)> { + let mut cursor = self.excerpts.cursor::(); + let point = Point::new(row, 0); + cursor.seek(&point, Bias::Right, &()); + if cursor.item().is_none() && *cursor.start() == point { + cursor.prev(&()); + } + if let Some(excerpt) = cursor.item() { + let overshoot = row - cursor.start().row; + let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer); + let excerpt_end = excerpt.range.context.end.to_point(&excerpt.buffer); + let buffer_row = excerpt_start.row + overshoot; + let line_start = Point::new(buffer_row, 0); + let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row)); + return Some(( + &excerpt.buffer, + line_start.max(excerpt_start)..line_end.min(excerpt_end), + )); + } + None + } + + pub fn max_point(&self) -> Point { + self.text_summary().lines + } + + pub fn text_summary(&self) -> TextSummary { + self.excerpts.summary().text.clone() + } + + pub fn text_summary_for_range(&self, range: Range) -> D + where + D: TextDimension, + O: ToOffset, + { + let mut summary = D::default(); + let mut range = range.start.to_offset(self)..range.end.to_offset(self); + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&range.start, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let mut end_before_newline = cursor.end(&()); + if excerpt.has_trailing_newline { + end_before_newline -= 1; + } + + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let start_in_excerpt = excerpt_start + (range.start - cursor.start()); + let end_in_excerpt = + excerpt_start + (cmp::min(end_before_newline, range.end) - cursor.start()); + summary.add_assign( + &excerpt + .buffer + .text_summary_for_range(start_in_excerpt..end_in_excerpt), + ); + + if range.end > end_before_newline { + summary.add_assign(&D::from_text_summary(&TextSummary::from("\n"))); + } + + cursor.next(&()); + } + + if range.end > *cursor.start() { + summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>( + &range.end, + Bias::Right, + &(), + ))); + if let Some(excerpt) = cursor.item() { + range.end = cmp::max(*cursor.start(), range.end); + + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let end_in_excerpt = excerpt_start + (range.end - cursor.start()); + summary.add_assign( + &excerpt + .buffer + .text_summary_for_range(excerpt_start..end_in_excerpt), + ); + } + } + + summary + } + + pub fn summary_for_anchor(&self, anchor: &Anchor) -> D + where + D: TextDimension + Ord + Sub, + { + let mut cursor = self.excerpts.cursor::(); + let locator = self.excerpt_locator_for_id(anchor.excerpt_id); + + cursor.seek(locator, Bias::Left, &()); + if cursor.item().is_none() { + cursor.next(&()); + } + + let mut position = D::from_text_summary(&cursor.start().text); + if let Some(excerpt) = cursor.item() { + if excerpt.id == anchor.excerpt_id { + let excerpt_buffer_start = + excerpt.range.context.start.summary::(&excerpt.buffer); + let excerpt_buffer_end = excerpt.range.context.end.summary::(&excerpt.buffer); + let buffer_position = cmp::min( + excerpt_buffer_end, + anchor.text_anchor.summary::(&excerpt.buffer), + ); + if buffer_position > excerpt_buffer_start { + position.add_assign(&(buffer_position - excerpt_buffer_start)); + } + } + } + position + } + + pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec + where + D: TextDimension + Ord + Sub, + I: 'a + IntoIterator, + { + if let Some((_, _, buffer)) = self.as_singleton() { + return buffer + .summaries_for_anchors(anchors.into_iter().map(|a| &a.text_anchor)) + .collect(); + } + + let mut anchors = anchors.into_iter().peekable(); + let mut cursor = self.excerpts.cursor::(); + let mut summaries = Vec::new(); + while let Some(anchor) = anchors.peek() { + let excerpt_id = anchor.excerpt_id; + let excerpt_anchors = iter::from_fn(|| { + let anchor = anchors.peek()?; + if anchor.excerpt_id == excerpt_id { + Some(&anchors.next().unwrap().text_anchor) + } else { + None + } + }); + + let locator = self.excerpt_locator_for_id(excerpt_id); + cursor.seek_forward(locator, Bias::Left, &()); + if cursor.item().is_none() { + cursor.next(&()); + } + + let position = D::from_text_summary(&cursor.start().text); + if let Some(excerpt) = cursor.item() { + if excerpt.id == excerpt_id { + let excerpt_buffer_start = + excerpt.range.context.start.summary::(&excerpt.buffer); + let excerpt_buffer_end = + excerpt.range.context.end.summary::(&excerpt.buffer); + summaries.extend( + excerpt + .buffer + .summaries_for_anchors::(excerpt_anchors) + .map(move |summary| { + let summary = cmp::min(excerpt_buffer_end.clone(), summary); + let mut position = position.clone(); + let excerpt_buffer_start = excerpt_buffer_start.clone(); + if summary > excerpt_buffer_start { + position.add_assign(&(summary - excerpt_buffer_start)); + } + position + }), + ); + continue; + } + } + + summaries.extend(excerpt_anchors.map(|_| position.clone())); + } + + summaries + } + + pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)> + where + I: 'a + IntoIterator, + { + let mut anchors = anchors.into_iter().enumerate().peekable(); + let mut cursor = self.excerpts.cursor::>(); + cursor.next(&()); + + let mut result = Vec::new(); + + while let Some((_, anchor)) = anchors.peek() { + let old_excerpt_id = anchor.excerpt_id; + + // Find the location where this anchor's excerpt should be. + let old_locator = self.excerpt_locator_for_id(old_excerpt_id); + cursor.seek_forward(&Some(old_locator), Bias::Left, &()); + + if cursor.item().is_none() { + cursor.next(&()); + } + + let next_excerpt = cursor.item(); + let prev_excerpt = cursor.prev_item(); + + // Process all of the anchors for this excerpt. + while let Some((_, anchor)) = anchors.peek() { + if anchor.excerpt_id != old_excerpt_id { + break; + } + let (anchor_ix, anchor) = anchors.next().unwrap(); + let mut anchor = *anchor; + + // Leave min and max anchors unchanged if invalid or + // if the old excerpt still exists at this location + let mut kept_position = next_excerpt + .map_or(false, |e| e.id == old_excerpt_id && e.contains(&anchor)) + || old_excerpt_id == ExcerptId::max() + || old_excerpt_id == ExcerptId::min(); + + // If the old excerpt no longer exists at this location, then attempt to + // find an equivalent position for this anchor in an adjacent excerpt. + if !kept_position { + for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) { + if excerpt.contains(&anchor) { + anchor.excerpt_id = excerpt.id.clone(); + kept_position = true; + break; + } + } + } + + // If there's no adjacent excerpt that contains the anchor's position, + // then report that the anchor has lost its position. + if !kept_position { + anchor = if let Some(excerpt) = next_excerpt { + let mut text_anchor = excerpt + .range + .context + .start + .bias(anchor.text_anchor.bias, &excerpt.buffer); + if text_anchor + .cmp(&excerpt.range.context.end, &excerpt.buffer) + .is_gt() + { + text_anchor = excerpt.range.context.end; + } + Anchor { + buffer_id: Some(excerpt.buffer_id), + excerpt_id: excerpt.id.clone(), + text_anchor, + } + } else if let Some(excerpt) = prev_excerpt { + let mut text_anchor = excerpt + .range + .context + .end + .bias(anchor.text_anchor.bias, &excerpt.buffer); + if text_anchor + .cmp(&excerpt.range.context.start, &excerpt.buffer) + .is_lt() + { + text_anchor = excerpt.range.context.start; + } + Anchor { + buffer_id: Some(excerpt.buffer_id), + excerpt_id: excerpt.id.clone(), + text_anchor, + } + } else if anchor.text_anchor.bias == Bias::Left { + Anchor::min() + } else { + Anchor::max() + }; + } + + result.push((anchor_ix, anchor, kept_position)); + } + } + result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self)); + result + } + + pub fn anchor_before(&self, position: T) -> Anchor { + self.anchor_at(position, Bias::Left) + } + + pub fn anchor_after(&self, position: T) -> Anchor { + self.anchor_at(position, Bias::Right) + } + + pub fn anchor_at(&self, position: T, mut bias: Bias) -> Anchor { + let offset = position.to_offset(self); + if let Some((excerpt_id, buffer_id, buffer)) = self.as_singleton() { + return Anchor { + buffer_id: Some(buffer_id), + excerpt_id: excerpt_id.clone(), + text_anchor: buffer.anchor_at(offset, bias), + }; + } + + let mut cursor = self.excerpts.cursor::<(usize, Option)>(); + cursor.seek(&offset, Bias::Right, &()); + if cursor.item().is_none() && offset == cursor.start().0 && bias == Bias::Left { + cursor.prev(&()); + } + if let Some(excerpt) = cursor.item() { + let mut overshoot = offset.saturating_sub(cursor.start().0); + if excerpt.has_trailing_newline && offset == cursor.end(&()).0 { + overshoot -= 1; + bias = Bias::Right; + } + + let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let text_anchor = + excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias)); + Anchor { + buffer_id: Some(excerpt.buffer_id), + excerpt_id: excerpt.id.clone(), + text_anchor, + } + } else if offset == 0 && bias == Bias::Left { + Anchor::min() + } else { + Anchor::max() + } + } + + pub fn anchor_in_excerpt(&self, excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Anchor { + let locator = self.excerpt_locator_for_id(excerpt_id); + let mut cursor = self.excerpts.cursor::>(); + cursor.seek(locator, Bias::Left, &()); + if let Some(excerpt) = cursor.item() { + if excerpt.id == excerpt_id { + let text_anchor = excerpt.clip_anchor(text_anchor); + drop(cursor); + return Anchor { + buffer_id: Some(excerpt.buffer_id), + excerpt_id, + text_anchor, + }; + } + } + panic!("excerpt not found"); + } + + pub fn can_resolve(&self, anchor: &Anchor) -> bool { + if anchor.excerpt_id == ExcerptId::min() || anchor.excerpt_id == ExcerptId::max() { + true + } else if let Some(excerpt) = self.excerpt(anchor.excerpt_id) { + excerpt.buffer.can_resolve(&anchor.text_anchor) + } else { + false + } + } + + pub fn excerpts( + &self, + ) -> impl Iterator)> { + self.excerpts + .iter() + .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone())) + } + + pub fn excerpt_boundaries_in_range( + &self, + range: R, + ) -> impl Iterator + '_ + where + R: RangeBounds, + T: ToOffset, + { + let start_offset; + let start = match range.start_bound() { + Bound::Included(start) => { + start_offset = start.to_offset(self); + Bound::Included(start_offset) + } + Bound::Excluded(start) => { + start_offset = start.to_offset(self); + Bound::Excluded(start_offset) + } + Bound::Unbounded => { + start_offset = 0; + Bound::Unbounded + } + }; + let end = match range.end_bound() { + Bound::Included(end) => Bound::Included(end.to_offset(self)), + Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)), + Bound::Unbounded => Bound::Unbounded, + }; + let bounds = (start, end); + + let mut cursor = self.excerpts.cursor::<(usize, Point)>(); + cursor.seek(&start_offset, Bias::Right, &()); + if cursor.item().is_none() { + cursor.prev(&()); + } + if !bounds.contains(&cursor.start().0) { + cursor.next(&()); + } + + let mut prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id); + std::iter::from_fn(move || { + if self.singleton { + None + } else if bounds.contains(&cursor.start().0) { + let excerpt = cursor.item()?; + let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id; + let boundary = ExcerptBoundary { + id: excerpt.id.clone(), + row: cursor.start().1.row, + buffer: excerpt.buffer.clone(), + range: excerpt.range.clone(), + starts_new_buffer, + }; + + prev_buffer_id = Some(excerpt.buffer_id); + cursor.next(&()); + Some(boundary) + } else { + None + } + }) + } + + pub fn edit_count(&self) -> usize { + self.edit_count + } + + pub fn parse_count(&self) -> usize { + self.parse_count + } + + /// Returns the smallest enclosing bracket ranges containing the given range or + /// None if no brackets contain range or the range is not contained in a single + /// excerpt + pub fn innermost_enclosing_bracket_ranges( + &self, + range: Range, + ) -> Option<(Range, Range)> { + let range = range.start.to_offset(self)..range.end.to_offset(self); + + // Get the ranges of the innermost pair of brackets. + let mut result: Option<(Range, Range)> = None; + + let Some(enclosing_bracket_ranges) = self.enclosing_bracket_ranges(range.clone()) else { + return None; + }; + + for (open, close) in enclosing_bracket_ranges { + let len = close.end - open.start; + + if let Some((existing_open, existing_close)) = &result { + let existing_len = existing_close.end - existing_open.start; + if len > existing_len { + continue; + } + } + + result = Some((open, close)); + } + + result + } + + /// Returns enclosing bracket ranges containing the given range or returns None if the range is + /// not contained in a single excerpt + pub fn enclosing_bracket_ranges<'a, T: ToOffset>( + &'a self, + range: Range, + ) -> Option, Range)> + 'a> { + let range = range.start.to_offset(self)..range.end.to_offset(self); + + self.bracket_ranges(range.clone()).map(|range_pairs| { + range_pairs + .filter(move |(open, close)| open.start <= range.start && close.end >= range.end) + }) + } + + /// Returns bracket range pairs overlapping the given `range` or returns None if the `range` is + /// not contained in a single excerpt + pub fn bracket_ranges<'a, T: ToOffset>( + &'a self, + range: Range, + ) -> Option, Range)> + 'a> { + let range = range.start.to_offset(self)..range.end.to_offset(self); + let excerpt = self.excerpt_containing(range.clone()); + excerpt.map(|(excerpt, excerpt_offset)| { + let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len; + + let start_in_buffer = excerpt_buffer_start + range.start.saturating_sub(excerpt_offset); + let end_in_buffer = excerpt_buffer_start + range.end.saturating_sub(excerpt_offset); + + excerpt + .buffer + .bracket_ranges(start_in_buffer..end_in_buffer) + .filter_map(move |(start_bracket_range, end_bracket_range)| { + if start_bracket_range.start < excerpt_buffer_start + || end_bracket_range.end > excerpt_buffer_end + { + return None; + } + + let mut start_bracket_range = start_bracket_range.clone(); + start_bracket_range.start = + excerpt_offset + (start_bracket_range.start - excerpt_buffer_start); + start_bracket_range.end = + excerpt_offset + (start_bracket_range.end - excerpt_buffer_start); + + let mut end_bracket_range = end_bracket_range.clone(); + end_bracket_range.start = + excerpt_offset + (end_bracket_range.start - excerpt_buffer_start); + end_bracket_range.end = + excerpt_offset + (end_bracket_range.end - excerpt_buffer_start); + Some((start_bracket_range, end_bracket_range)) + }) + }) + } + + pub fn diagnostics_update_count(&self) -> usize { + self.diagnostics_update_count + } + + pub fn git_diff_update_count(&self) -> usize { + self.git_diff_update_count + } + + pub fn trailing_excerpt_update_count(&self) -> usize { + self.trailing_excerpt_update_count + } + + pub fn file_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc> { + self.point_to_buffer_offset(point) + .and_then(|(buffer, _)| buffer.file()) + } + + pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc> { + self.point_to_buffer_offset(point) + .and_then(|(buffer, offset)| buffer.language_at(offset)) + } + + pub fn settings_at<'a, T: ToOffset>( + &'a self, + point: T, + cx: &'a AppContext, + ) -> &'a LanguageSettings { + let mut language = None; + let mut file = None; + if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { + language = buffer.language_at(offset); + file = buffer.file(); + } + language_settings(language, file, cx) + } + + pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { + self.point_to_buffer_offset(point) + .and_then(|(buffer, offset)| buffer.language_scope_at(offset)) + } + + pub fn language_indent_size_at( + &self, + position: T, + cx: &AppContext, + ) -> Option { + let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?; + Some(buffer_snapshot.language_indent_size_at(offset, cx)) + } + + pub fn is_dirty(&self) -> bool { + self.is_dirty + } + + pub fn has_conflict(&self) -> bool { + self.has_conflict + } + + pub fn diagnostic_group<'a, O>( + &'a self, + group_id: usize, + ) -> impl Iterator> + 'a + where + O: text::FromAnchor + 'a, + { + self.as_singleton() + .into_iter() + .flat_map(move |(_, _, buffer)| buffer.diagnostic_group(group_id)) + } + + pub fn diagnostics_in_range<'a, T, O>( + &'a self, + range: Range, + reversed: bool, + ) -> impl Iterator> + 'a + where + T: 'a + ToOffset, + O: 'a + text::FromAnchor + Ord, + { + self.as_singleton() + .into_iter() + .flat_map(move |(_, _, buffer)| { + buffer.diagnostics_in_range( + range.start.to_offset(self)..range.end.to_offset(self), + reversed, + ) + }) + } + + pub fn has_git_diffs(&self) -> bool { + for excerpt in self.excerpts.iter() { + if !excerpt.buffer.git_diff.is_empty() { + return true; + } + } + false + } + + pub fn git_diff_hunks_in_range_rev<'a>( + &'a self, + row_range: Range, + ) -> impl 'a + Iterator> { + let mut cursor = self.excerpts.cursor::(); + + cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &()); + if cursor.item().is_none() { + cursor.prev(&()); + } + + std::iter::from_fn(move || { + let excerpt = cursor.item()?; + let multibuffer_start = *cursor.start(); + let multibuffer_end = multibuffer_start + excerpt.text_summary.lines; + if multibuffer_start.row >= row_range.end { + return None; + } + + let mut buffer_start = excerpt.range.context.start; + let mut buffer_end = excerpt.range.context.end; + let excerpt_start_point = buffer_start.to_point(&excerpt.buffer); + let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines; + + if row_range.start > multibuffer_start.row { + let buffer_start_point = + excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0); + buffer_start = excerpt.buffer.anchor_before(buffer_start_point); + } + + if row_range.end < multibuffer_end.row { + let buffer_end_point = + excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0); + buffer_end = excerpt.buffer.anchor_before(buffer_end_point); + } + + let buffer_hunks = excerpt + .buffer + .git_diff_hunks_intersecting_range_rev(buffer_start..buffer_end) + .filter_map(move |hunk| { + let start = multibuffer_start.row + + hunk + .buffer_range + .start + .saturating_sub(excerpt_start_point.row); + let end = multibuffer_start.row + + hunk + .buffer_range + .end + .min(excerpt_end_point.row + 1) + .saturating_sub(excerpt_start_point.row); + + Some(DiffHunk { + buffer_range: start..end, + diff_base_byte_range: hunk.diff_base_byte_range.clone(), + }) + }); + + cursor.prev(&()); + + Some(buffer_hunks) + }) + .flatten() + } + + pub fn git_diff_hunks_in_range<'a>( + &'a self, + row_range: Range, + ) -> impl 'a + Iterator> { + let mut cursor = self.excerpts.cursor::(); + + cursor.seek(&Point::new(row_range.start, 0), Bias::Right, &()); + + std::iter::from_fn(move || { + let excerpt = cursor.item()?; + let multibuffer_start = *cursor.start(); + let multibuffer_end = multibuffer_start + excerpt.text_summary.lines; + if multibuffer_start.row >= row_range.end { + return None; + } + + let mut buffer_start = excerpt.range.context.start; + let mut buffer_end = excerpt.range.context.end; + let excerpt_start_point = buffer_start.to_point(&excerpt.buffer); + let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines; + + if row_range.start > multibuffer_start.row { + let buffer_start_point = + excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0); + buffer_start = excerpt.buffer.anchor_before(buffer_start_point); + } + + if row_range.end < multibuffer_end.row { + let buffer_end_point = + excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0); + buffer_end = excerpt.buffer.anchor_before(buffer_end_point); + } + + let buffer_hunks = excerpt + .buffer + .git_diff_hunks_intersecting_range(buffer_start..buffer_end) + .filter_map(move |hunk| { + let start = multibuffer_start.row + + hunk + .buffer_range + .start + .saturating_sub(excerpt_start_point.row); + let end = multibuffer_start.row + + hunk + .buffer_range + .end + .min(excerpt_end_point.row + 1) + .saturating_sub(excerpt_start_point.row); + + Some(DiffHunk { + buffer_range: start..end, + diff_base_byte_range: hunk.diff_base_byte_range.clone(), + }) + }); + + cursor.next(&()); + + Some(buffer_hunks) + }) + .flatten() + } + + pub fn range_for_syntax_ancestor(&self, range: Range) -> Option> { + let range = range.start.to_offset(self)..range.end.to_offset(self); + + self.excerpt_containing(range.clone()) + .and_then(|(excerpt, excerpt_offset)| { + let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len; + + let start_in_buffer = + excerpt_buffer_start + range.start.saturating_sub(excerpt_offset); + let end_in_buffer = excerpt_buffer_start + range.end.saturating_sub(excerpt_offset); + let mut ancestor_buffer_range = excerpt + .buffer + .range_for_syntax_ancestor(start_in_buffer..end_in_buffer)?; + ancestor_buffer_range.start = + cmp::max(ancestor_buffer_range.start, excerpt_buffer_start); + ancestor_buffer_range.end = cmp::min(ancestor_buffer_range.end, excerpt_buffer_end); + + let start = excerpt_offset + (ancestor_buffer_range.start - excerpt_buffer_start); + let end = excerpt_offset + (ancestor_buffer_range.end - excerpt_buffer_start); + Some(start..end) + }) + } + + pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option> { + let (excerpt_id, _, buffer) = self.as_singleton()?; + let outline = buffer.outline(theme)?; + Some(Outline::new( + outline + .items + .into_iter() + .map(|item| OutlineItem { + depth: item.depth, + range: self.anchor_in_excerpt(excerpt_id.clone(), item.range.start) + ..self.anchor_in_excerpt(excerpt_id.clone(), item.range.end), + text: item.text, + highlight_ranges: item.highlight_ranges, + name_ranges: item.name_ranges, + }) + .collect(), + )) + } + + pub fn symbols_containing( + &self, + offset: T, + theme: Option<&SyntaxTheme>, + ) -> Option<(u64, Vec>)> { + let anchor = self.anchor_before(offset); + let excerpt_id = anchor.excerpt_id; + let excerpt = self.excerpt(excerpt_id)?; + Some(( + excerpt.buffer_id, + excerpt + .buffer + .symbols_containing(anchor.text_anchor, theme) + .into_iter() + .flatten() + .map(|item| OutlineItem { + depth: item.depth, + range: self.anchor_in_excerpt(excerpt_id, item.range.start) + ..self.anchor_in_excerpt(excerpt_id, item.range.end), + text: item.text, + highlight_ranges: item.highlight_ranges, + name_ranges: item.name_ranges, + }) + .collect(), + )) + } + + fn excerpt_locator_for_id<'a>(&'a self, id: ExcerptId) -> &'a Locator { + if id == ExcerptId::min() { + Locator::min_ref() + } else if id == ExcerptId::max() { + Locator::max_ref() + } else { + let mut cursor = self.excerpt_ids.cursor::(); + cursor.seek(&id, Bias::Left, &()); + if let Some(entry) = cursor.item() { + if entry.id == id { + return &entry.locator; + } + } + panic!("invalid excerpt id {:?}", id) + } + } + + pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option { + Some(self.excerpt(excerpt_id)?.buffer_id) + } + + pub fn buffer_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<&BufferSnapshot> { + Some(&self.excerpt(excerpt_id)?.buffer) + } + + fn excerpt<'a>(&'a self, excerpt_id: ExcerptId) -> Option<&'a Excerpt> { + let mut cursor = self.excerpts.cursor::>(); + let locator = self.excerpt_locator_for_id(excerpt_id); + cursor.seek(&Some(locator), Bias::Left, &()); + if let Some(excerpt) = cursor.item() { + if excerpt.id == excerpt_id { + return Some(excerpt); + } + } + None + } + + /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts + fn excerpt_containing<'a, T: ToOffset>( + &'a self, + range: Range, + ) -> Option<(&'a Excerpt, usize)> { + let range = range.start.to_offset(self)..range.end.to_offset(self); + + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&range.start, Bias::Right, &()); + let start_excerpt = cursor.item(); + + if range.start == range.end { + return start_excerpt.map(|excerpt| (excerpt, *cursor.start())); + } + + cursor.seek(&range.end, Bias::Right, &()); + let end_excerpt = cursor.item(); + + start_excerpt + .zip(end_excerpt) + .and_then(|(start_excerpt, end_excerpt)| { + if start_excerpt.id != end_excerpt.id { + return None; + } + + Some((start_excerpt, *cursor.start())) + }) + } + + pub fn remote_selections_in_range<'a>( + &'a self, + range: &'a Range, + ) -> impl 'a + Iterator)> { + let mut cursor = self.excerpts.cursor::(); + let start_locator = self.excerpt_locator_for_id(range.start.excerpt_id); + let end_locator = self.excerpt_locator_for_id(range.end.excerpt_id); + cursor.seek(start_locator, Bias::Left, &()); + cursor + .take_while(move |excerpt| excerpt.locator <= *end_locator) + .flat_map(move |excerpt| { + let mut query_range = excerpt.range.context.start..excerpt.range.context.end; + if excerpt.id == range.start.excerpt_id { + query_range.start = range.start.text_anchor; + } + if excerpt.id == range.end.excerpt_id { + query_range.end = range.end.text_anchor; + } + + excerpt + .buffer + .remote_selections_in_range(query_range) + .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| { + selections.map(move |selection| { + let mut start = Anchor { + buffer_id: Some(excerpt.buffer_id), + excerpt_id: excerpt.id.clone(), + text_anchor: selection.start, + }; + let mut end = Anchor { + buffer_id: Some(excerpt.buffer_id), + excerpt_id: excerpt.id.clone(), + text_anchor: selection.end, + }; + if range.start.cmp(&start, self).is_gt() { + start = range.start.clone(); + } + if range.end.cmp(&end, self).is_lt() { + end = range.end.clone(); + } + + ( + replica_id, + line_mode, + cursor_shape, + Selection { + id: selection.id, + start, + end, + reversed: selection.reversed, + goal: selection.goal, + }, + ) + }) + }) + }) + } +} + +#[cfg(any(test, feature = "test-support"))] +impl MultiBufferSnapshot { + pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range { + let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right); + let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right); + start..end + } +} + +impl History { + fn start_transaction(&mut self, now: Instant) -> Option { + self.transaction_depth += 1; + if self.transaction_depth == 1 { + let id = self.next_transaction_id.tick(); + self.undo_stack.push(Transaction { + id, + buffer_transactions: Default::default(), + first_edit_at: now, + last_edit_at: now, + suppress_grouping: false, + }); + Some(id) + } else { + None + } + } + + fn end_transaction( + &mut self, + now: Instant, + buffer_transactions: HashMap, + ) -> bool { + assert_ne!(self.transaction_depth, 0); + self.transaction_depth -= 1; + if self.transaction_depth == 0 { + if buffer_transactions.is_empty() { + self.undo_stack.pop(); + false + } else { + self.redo_stack.clear(); + let transaction = self.undo_stack.last_mut().unwrap(); + transaction.last_edit_at = now; + for (buffer_id, transaction_id) in buffer_transactions { + transaction + .buffer_transactions + .entry(buffer_id) + .or_insert(transaction_id); + } + true + } + } else { + false + } + } + + fn push_transaction<'a, T>( + &mut self, + buffer_transactions: T, + now: Instant, + cx: &mut ModelContext, + ) where + T: IntoIterator, &'a language2::Transaction)>, + { + assert_eq!(self.transaction_depth, 0); + let transaction = Transaction { + id: self.next_transaction_id.tick(), + buffer_transactions: buffer_transactions + .into_iter() + .map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id)) + .collect(), + first_edit_at: now, + last_edit_at: now, + suppress_grouping: false, + }; + if !transaction.buffer_transactions.is_empty() { + self.undo_stack.push(transaction); + self.redo_stack.clear(); + } + } + + fn finalize_last_transaction(&mut self) { + if let Some(transaction) = self.undo_stack.last_mut() { + transaction.suppress_grouping = true; + } + } + + fn forget(&mut self, transaction_id: TransactionId) -> Option { + if let Some(ix) = self + .undo_stack + .iter() + .rposition(|transaction| transaction.id == transaction_id) + { + Some(self.undo_stack.remove(ix)) + } else if let Some(ix) = self + .redo_stack + .iter() + .rposition(|transaction| transaction.id == transaction_id) + { + Some(self.redo_stack.remove(ix)) + } else { + None + } + } + + fn transaction_mut(&mut self, transaction_id: TransactionId) -> Option<&mut Transaction> { + self.undo_stack + .iter_mut() + .find(|transaction| transaction.id == transaction_id) + .or_else(|| { + self.redo_stack + .iter_mut() + .find(|transaction| transaction.id == transaction_id) + }) + } + + fn pop_undo(&mut self) -> Option<&mut Transaction> { + assert_eq!(self.transaction_depth, 0); + if let Some(transaction) = self.undo_stack.pop() { + self.redo_stack.push(transaction); + self.redo_stack.last_mut() + } else { + None + } + } + + fn pop_redo(&mut self) -> Option<&mut Transaction> { + assert_eq!(self.transaction_depth, 0); + if let Some(transaction) = self.redo_stack.pop() { + self.undo_stack.push(transaction); + self.undo_stack.last_mut() + } else { + None + } + } + + fn remove_from_undo(&mut self, transaction_id: TransactionId) -> Option<&Transaction> { + let ix = self + .undo_stack + .iter() + .rposition(|transaction| transaction.id == transaction_id)?; + let transaction = self.undo_stack.remove(ix); + self.redo_stack.push(transaction); + self.redo_stack.last() + } + + fn group(&mut self) -> Option { + let mut count = 0; + let mut transactions = self.undo_stack.iter(); + if let Some(mut transaction) = transactions.next_back() { + while let Some(prev_transaction) = transactions.next_back() { + if !prev_transaction.suppress_grouping + && transaction.first_edit_at - prev_transaction.last_edit_at + <= self.group_interval + { + transaction = prev_transaction; + count += 1; + } else { + break; + } + } + } + self.group_trailing(count) + } + + fn group_until(&mut self, transaction_id: TransactionId) { + let mut count = 0; + for transaction in self.undo_stack.iter().rev() { + if transaction.id == transaction_id { + self.group_trailing(count); + break; + } else if transaction.suppress_grouping { + break; + } else { + count += 1; + } + } + } + + fn group_trailing(&mut self, n: usize) -> Option { + let new_len = self.undo_stack.len() - n; + let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len); + if let Some(last_transaction) = transactions_to_keep.last_mut() { + if let Some(transaction) = transactions_to_merge.last() { + last_transaction.last_edit_at = transaction.last_edit_at; + } + for to_merge in transactions_to_merge { + for (buffer_id, transaction_id) in &to_merge.buffer_transactions { + last_transaction + .buffer_transactions + .entry(*buffer_id) + .or_insert(*transaction_id); + } + } + } + + self.undo_stack.truncate(new_len); + self.undo_stack.last().map(|t| t.id) + } +} + +impl Excerpt { + fn new( + id: ExcerptId, + locator: Locator, + buffer_id: u64, + buffer: BufferSnapshot, + range: ExcerptRange, + has_trailing_newline: bool, + ) -> Self { + Excerpt { + id, + locator, + max_buffer_row: range.context.end.to_point(&buffer).row, + text_summary: buffer + .text_summary_for_range::(range.context.to_offset(&buffer)), + buffer_id, + buffer, + range, + has_trailing_newline, + } + } + + fn chunks_in_range(&self, range: Range, language_aware: bool) -> ExcerptChunks { + let content_start = self.range.context.start.to_offset(&self.buffer); + let chunks_start = content_start + range.start; + let chunks_end = content_start + cmp::min(range.end, self.text_summary.len); + + let footer_height = if self.has_trailing_newline + && range.start <= self.text_summary.len + && range.end > self.text_summary.len + { + 1 + } else { + 0 + }; + + let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware); + + ExcerptChunks { + content_chunks, + footer_height, + } + } + + fn bytes_in_range(&self, range: Range) -> ExcerptBytes { + let content_start = self.range.context.start.to_offset(&self.buffer); + let bytes_start = content_start + range.start; + let bytes_end = content_start + cmp::min(range.end, self.text_summary.len); + let footer_height = if self.has_trailing_newline + && range.start <= self.text_summary.len + && range.end > self.text_summary.len + { + 1 + } else { + 0 + }; + let content_bytes = self.buffer.bytes_in_range(bytes_start..bytes_end); + + ExcerptBytes { + content_bytes, + footer_height, + } + } + + fn reversed_bytes_in_range(&self, range: Range) -> ExcerptBytes { + let content_start = self.range.context.start.to_offset(&self.buffer); + let bytes_start = content_start + range.start; + let bytes_end = content_start + cmp::min(range.end, self.text_summary.len); + let footer_height = if self.has_trailing_newline + && range.start <= self.text_summary.len + && range.end > self.text_summary.len + { + 1 + } else { + 0 + }; + let content_bytes = self.buffer.reversed_bytes_in_range(bytes_start..bytes_end); + + ExcerptBytes { + content_bytes, + footer_height, + } + } + + fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor { + if text_anchor + .cmp(&self.range.context.start, &self.buffer) + .is_lt() + { + self.range.context.start + } else if text_anchor + .cmp(&self.range.context.end, &self.buffer) + .is_gt() + { + self.range.context.end + } else { + text_anchor + } + } + + fn contains(&self, anchor: &Anchor) -> bool { + Some(self.buffer_id) == anchor.buffer_id + && self + .range + .context + .start + .cmp(&anchor.text_anchor, &self.buffer) + .is_le() + && self + .range + .context + .end + .cmp(&anchor.text_anchor, &self.buffer) + .is_ge() + } +} + +impl ExcerptId { + pub fn min() -> Self { + Self(0) + } + + pub fn max() -> Self { + Self(usize::MAX) + } + + pub fn to_proto(&self) -> u64 { + self.0 as _ + } + + pub fn from_proto(proto: u64) -> Self { + Self(proto as _) + } + + pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering { + let a = snapshot.excerpt_locator_for_id(*self); + let b = snapshot.excerpt_locator_for_id(*other); + a.cmp(&b).then_with(|| self.0.cmp(&other.0)) + } +} + +impl Into for ExcerptId { + fn into(self) -> usize { + self.0 + } +} + +impl fmt::Debug for Excerpt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Excerpt") + .field("id", &self.id) + .field("locator", &self.locator) + .field("buffer_id", &self.buffer_id) + .field("range", &self.range) + .field("text_summary", &self.text_summary) + .field("has_trailing_newline", &self.has_trailing_newline) + .finish() + } +} + +impl sum_tree::Item for Excerpt { + type Summary = ExcerptSummary; + + fn summary(&self) -> Self::Summary { + let mut text = self.text_summary.clone(); + if self.has_trailing_newline { + text += TextSummary::from("\n"); + } + ExcerptSummary { + excerpt_id: self.id, + excerpt_locator: self.locator.clone(), + max_buffer_row: self.max_buffer_row, + text, + } + } +} + +impl sum_tree::Item for ExcerptIdMapping { + type Summary = ExcerptId; + + fn summary(&self) -> Self::Summary { + self.id + } +} + +impl sum_tree::KeyedItem for ExcerptIdMapping { + type Key = ExcerptId; + + fn key(&self) -> Self::Key { + self.id + } +} + +impl sum_tree::Summary for ExcerptId { + type Context = (); + + fn add_summary(&mut self, other: &Self, _: &()) { + *self = *other; + } +} + +impl sum_tree::Summary for ExcerptSummary { + type Context = (); + + fn add_summary(&mut self, summary: &Self, _: &()) { + debug_assert!(summary.excerpt_locator > self.excerpt_locator); + self.excerpt_locator = summary.excerpt_locator.clone(); + self.text.add_summary(&summary.text, &()); + self.max_buffer_row = cmp::max(self.max_buffer_row, summary.max_buffer_row); + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self += &summary.text; + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self += summary.text.len; + } +} + +impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize { + fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering { + Ord::cmp(self, &cursor_location.text.len) + } +} + +impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator { + fn cmp(&self, cursor_location: &Option<&'a Locator>, _: &()) -> cmp::Ordering { + Ord::cmp(&Some(self), cursor_location) + } +} + +impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Locator { + fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering { + Ord::cmp(self, &cursor_location.excerpt_locator) + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for OffsetUtf16 { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self += summary.text.len_utf16; + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self += summary.text.lines; + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self += summary.text.lines_utf16() + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a Locator> { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self = Some(&summary.excerpt_locator); + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option { + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) { + *self = Some(summary.excerpt_id); + } +} + +impl<'a> MultiBufferRows<'a> { + pub fn seek(&mut self, row: u32) { + self.buffer_row_range = 0..0; + + self.excerpts + .seek_forward(&Point::new(row, 0), Bias::Right, &()); + if self.excerpts.item().is_none() { + self.excerpts.prev(&()); + + if self.excerpts.item().is_none() && row == 0 { + self.buffer_row_range = 0..1; + return; + } + } + + if let Some(excerpt) = self.excerpts.item() { + let overshoot = row - self.excerpts.start().row; + let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer).row; + self.buffer_row_range.start = excerpt_start + overshoot; + self.buffer_row_range.end = excerpt_start + excerpt.text_summary.lines.row + 1; + } + } +} + +impl<'a> Iterator for MultiBufferRows<'a> { + type Item = Option; + + fn next(&mut self) -> Option { + loop { + if !self.buffer_row_range.is_empty() { + let row = Some(self.buffer_row_range.start); + self.buffer_row_range.start += 1; + return Some(row); + } + self.excerpts.item()?; + self.excerpts.next(&()); + let excerpt = self.excerpts.item()?; + self.buffer_row_range.start = excerpt.range.context.start.to_point(&excerpt.buffer).row; + self.buffer_row_range.end = + self.buffer_row_range.start + excerpt.text_summary.lines.row + 1; + } + } +} + +impl<'a> MultiBufferChunks<'a> { + pub fn offset(&self) -> usize { + self.range.start + } + + pub fn seek(&mut self, offset: usize) { + self.range.start = offset; + self.excerpts.seek(&offset, Bias::Right, &()); + if let Some(excerpt) = self.excerpts.item() { + self.excerpt_chunks = Some(excerpt.chunks_in_range( + self.range.start - self.excerpts.start()..self.range.end - self.excerpts.start(), + self.language_aware, + )); + } else { + self.excerpt_chunks = None; + } + } +} + +impl<'a> Iterator for MultiBufferChunks<'a> { + type Item = Chunk<'a>; + + fn next(&mut self) -> Option { + if self.range.is_empty() { + None + } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() { + self.range.start += chunk.text.len(); + Some(chunk) + } else { + self.excerpts.next(&()); + let excerpt = self.excerpts.item()?; + self.excerpt_chunks = Some(excerpt.chunks_in_range( + 0..self.range.end - self.excerpts.start(), + self.language_aware, + )); + self.next() + } + } +} + +impl<'a> MultiBufferBytes<'a> { + fn consume(&mut self, len: usize) { + self.range.start += len; + self.chunk = &self.chunk[len..]; + + if !self.range.is_empty() && self.chunk.is_empty() { + if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) { + self.chunk = chunk; + } else { + self.excerpts.next(&()); + if let Some(excerpt) = self.excerpts.item() { + let mut excerpt_bytes = + excerpt.bytes_in_range(0..self.range.end - self.excerpts.start()); + self.chunk = excerpt_bytes.next().unwrap(); + self.excerpt_bytes = Some(excerpt_bytes); + } + } + } + } +} + +impl<'a> Iterator for MultiBufferBytes<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + let chunk = self.chunk; + if chunk.is_empty() { + None + } else { + self.consume(chunk.len()); + Some(chunk) + } + } +} + +impl<'a> io::Read for MultiBufferBytes<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = cmp::min(buf.len(), self.chunk.len()); + buf[..len].copy_from_slice(&self.chunk[..len]); + if len > 0 { + self.consume(len); + } + Ok(len) + } +} + +impl<'a> ReversedMultiBufferBytes<'a> { + fn consume(&mut self, len: usize) { + self.range.end -= len; + self.chunk = &self.chunk[..self.chunk.len() - len]; + + if !self.range.is_empty() && self.chunk.is_empty() { + if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) { + self.chunk = chunk; + } else { + self.excerpts.next(&()); + if let Some(excerpt) = self.excerpts.item() { + let mut excerpt_bytes = + excerpt.bytes_in_range(0..self.range.end - self.excerpts.start()); + self.chunk = excerpt_bytes.next().unwrap(); + self.excerpt_bytes = Some(excerpt_bytes); + } + } + } + } +} + +impl<'a> io::Read for ReversedMultiBufferBytes<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = cmp::min(buf.len(), self.chunk.len()); + buf[..len].copy_from_slice(&self.chunk[..len]); + buf[..len].reverse(); + if len > 0 { + self.consume(len); + } + Ok(len) + } +} +impl<'a> Iterator for ExcerptBytes<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + if let Some(chunk) = self.content_bytes.next() { + if !chunk.is_empty() { + return Some(chunk); + } + } + + if self.footer_height > 0 { + let result = &NEWLINES[..self.footer_height]; + self.footer_height = 0; + return Some(result); + } + + None + } +} + +impl<'a> Iterator for ExcerptChunks<'a> { + type Item = Chunk<'a>; + + fn next(&mut self) -> Option { + if let Some(chunk) = self.content_chunks.next() { + return Some(chunk); + } + + if self.footer_height > 0 { + let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.footer_height]) }; + self.footer_height = 0; + return Some(Chunk { + text, + ..Default::default() + }); + } + + None + } +} + +impl ToOffset for Point { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + snapshot.point_to_offset(*self) + } +} + +impl ToOffset for usize { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + assert!(*self <= snapshot.len(), "offset is out of range"); + *self + } +} + +impl ToOffset for OffsetUtf16 { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + snapshot.offset_utf16_to_offset(*self) + } +} + +impl ToOffset for PointUtf16 { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + snapshot.point_utf16_to_offset(*self) + } +} + +impl ToOffsetUtf16 for OffsetUtf16 { + fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + *self + } +} + +impl ToOffsetUtf16 for usize { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + snapshot.offset_to_offset_utf16(*self) + } +} + +impl ToPoint for usize { + fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point { + snapshot.offset_to_point(*self) + } +} + +impl ToPoint for Point { + fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point { + *self + } +} + +impl ToPointUtf16 for usize { + fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 { + snapshot.offset_to_point_utf16(*self) + } +} + +impl ToPointUtf16 for Point { + fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 { + snapshot.point_to_point_utf16(*self) + } +} + +impl ToPointUtf16 for PointUtf16 { + fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 { + *self + } +} + +fn build_excerpt_ranges( + buffer: &BufferSnapshot, + ranges: &[Range], + context_line_count: u32, +) -> (Vec>, Vec) +where + T: text::ToPoint, +{ + let max_point = buffer.max_point(); + let mut range_counts = Vec::new(); + let mut excerpt_ranges = Vec::new(); + let mut range_iter = ranges + .iter() + .map(|range| range.start.to_point(buffer)..range.end.to_point(buffer)) + .peekable(); + while let Some(range) = range_iter.next() { + let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0); + let mut excerpt_end = Point::new(range.end.row + 1 + context_line_count, 0).min(max_point); + let mut ranges_in_excerpt = 1; + + while let Some(next_range) = range_iter.peek() { + if next_range.start.row <= excerpt_end.row + context_line_count { + excerpt_end = + Point::new(next_range.end.row + 1 + context_line_count, 0).min(max_point); + ranges_in_excerpt += 1; + range_iter.next(); + } else { + break; + } + } + + excerpt_ranges.push(ExcerptRange { + context: excerpt_start..excerpt_end, + primary: Some(range), + }); + range_counts.push(ranges_in_excerpt); + } + + (excerpt_ranges, range_counts) +} + +#[cfg(test)] +mod tests { + use super::*; + use futures::StreamExt; + use gpui2::{AppContext, Context, TestAppContext}; + use language2::{Buffer, Rope}; + use parking_lot::RwLock; + use rand::prelude::*; + use settings2::SettingsStore; + use std::env; + use util::test::sample_text; + + #[gpui2::test] + fn test_singleton(cx: &mut AppContext) { + let buffer = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a'))); + let multibuffer = cx.build_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot.text(), buffer.read(cx).text()); + + assert_eq!( + snapshot.buffer_rows(0).collect::>(), + (0..buffer.read(cx).row_count()) + .map(Some) + .collect::>() + ); + + buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx)); + let snapshot = multibuffer.read(cx).snapshot(cx); + + assert_eq!(snapshot.text(), buffer.read(cx).text()); + assert_eq!( + snapshot.buffer_rows(0).collect::>(), + (0..buffer.read(cx).row_count()) + .map(Some) + .collect::>() + ); + } + + #[gpui2::test] + fn test_remote(cx: &mut AppContext) { + let host_buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a")); + let guest_buffer = cx.build_model(|cx| { + let state = host_buffer.read(cx).to_proto(); + let ops = cx + .executor() + .block(host_buffer.read(cx).serialize_ops(None, cx)); + let mut buffer = Buffer::from_proto(1, state, None).unwrap(); + buffer + .apply_ops( + ops.into_iter() + .map(|op| language2::proto::deserialize_operation(op).unwrap()), + cx, + ) + .unwrap(); + buffer + }); + let multibuffer = cx.build_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx)); + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot.text(), "a"); + + guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx)); + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot.text(), "ab"); + + guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx)); + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot.text(), "abc"); + } + + #[gpui2::test] + fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) { + let buffer_1 = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a'))); + let buffer_2 = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g'))); + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + + let events = Arc::new(RwLock::new(Vec::::new())); + multibuffer.update(cx, |_, cx| { + let events = events.clone(); + cx.subscribe(&multibuffer, move |_, _, event, _| { + if let Event::Edited { .. } = event { + events.write().push(event.clone()) + } + }) + .detach(); + }); + + let subscription = multibuffer.update(cx, |multibuffer, cx| { + let subscription = multibuffer.subscribe(); + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: Point::new(1, 2)..Point::new(2, 5), + primary: None, + }], + cx, + ); + assert_eq!( + subscription.consume().into_inner(), + [Edit { + old: 0..0, + new: 0..10 + }] + ); + + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: Point::new(3, 3)..Point::new(4, 4), + primary: None, + }], + cx, + ); + multibuffer.push_excerpts( + buffer_2.clone(), + [ExcerptRange { + context: Point::new(3, 1)..Point::new(3, 3), + primary: None, + }], + cx, + ); + assert_eq!( + subscription.consume().into_inner(), + [Edit { + old: 10..10, + new: 10..22 + }] + ); + + subscription + }); + + // Adding excerpts emits an edited event. + assert_eq!( + events.read().as_slice(), + &[ + Event::Edited { + sigleton_buffer_edited: false + }, + Event::Edited { + sigleton_buffer_edited: false + }, + Event::Edited { + sigleton_buffer_edited: false + } + ] + ); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!( + snapshot.text(), + concat!( + "bbbb\n", // Preserve newlines + "ccccc\n", // + "ddd\n", // + "eeee\n", // + "jj" // + ) + ); + assert_eq!( + snapshot.buffer_rows(0).collect::>(), + [Some(1), Some(2), Some(3), Some(4), Some(3)] + ); + assert_eq!( + snapshot.buffer_rows(2).collect::>(), + [Some(3), Some(4), Some(3)] + ); + assert_eq!(snapshot.buffer_rows(4).collect::>(), [Some(3)]); + assert_eq!(snapshot.buffer_rows(5).collect::>(), []); + + assert_eq!( + boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot), + &[ + (0, "bbbb\nccccc".to_string(), true), + (2, "ddd\neeee".to_string(), false), + (4, "jj".to_string(), true), + ] + ); + assert_eq!( + boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot), + &[(0, "bbbb\nccccc".to_string(), true)] + ); + assert_eq!( + boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot), + &[] + ); + assert_eq!( + boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot), + &[] + ); + assert_eq!( + boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot), + &[(2, "ddd\neeee".to_string(), false)] + ); + assert_eq!( + boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot), + &[(2, "ddd\neeee".to_string(), false)] + ); + assert_eq!( + boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot), + &[(2, "ddd\neeee".to_string(), false)] + ); + assert_eq!( + boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot), + &[(4, "jj".to_string(), true)] + ); + assert_eq!( + boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot), + &[] + ); + + buffer_1.update(cx, |buffer, cx| { + let text = "\n"; + buffer.edit( + [ + (Point::new(0, 0)..Point::new(0, 0), text), + (Point::new(2, 1)..Point::new(2, 3), text), + ], + None, + cx, + ); + }); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!( + snapshot.text(), + concat!( + "bbbb\n", // Preserve newlines + "c\n", // + "cc\n", // + "ddd\n", // + "eeee\n", // + "jj" // + ) + ); + + assert_eq!( + subscription.consume().into_inner(), + [Edit { + old: 6..8, + new: 6..7 + }] + ); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!( + snapshot.clip_point(Point::new(0, 5), Bias::Left), + Point::new(0, 4) + ); + assert_eq!( + snapshot.clip_point(Point::new(0, 5), Bias::Right), + Point::new(0, 4) + ); + assert_eq!( + snapshot.clip_point(Point::new(5, 1), Bias::Right), + Point::new(5, 1) + ); + assert_eq!( + snapshot.clip_point(Point::new(5, 2), Bias::Right), + Point::new(5, 2) + ); + assert_eq!( + snapshot.clip_point(Point::new(5, 3), Bias::Right), + Point::new(5, 2) + ); + + let snapshot = multibuffer.update(cx, |multibuffer, cx| { + let (buffer_2_excerpt_id, _) = + multibuffer.excerpts_for_buffer(&buffer_2, cx)[0].clone(); + multibuffer.remove_excerpts([buffer_2_excerpt_id], cx); + multibuffer.snapshot(cx) + }); + + assert_eq!( + snapshot.text(), + concat!( + "bbbb\n", // Preserve newlines + "c\n", // + "cc\n", // + "ddd\n", // + "eeee", // + ) + ); + + fn boundaries_in_range( + range: Range, + snapshot: &MultiBufferSnapshot, + ) -> Vec<(u32, String, bool)> { + snapshot + .excerpt_boundaries_in_range(range) + .map(|boundary| { + ( + boundary.row, + boundary + .buffer + .text_for_range(boundary.range.context) + .collect::(), + boundary.starts_new_buffer, + ) + }) + .collect::>() + } + } + + #[gpui2::test] + fn test_excerpt_events(cx: &mut AppContext) { + let buffer_1 = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'a'))); + let buffer_2 = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm'))); + + let leader_multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + let follower_multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + let follower_edit_event_count = Arc::new(RwLock::new(0)); + + follower_multibuffer.update(cx, |_, cx| { + let follower_edit_event_count = follower_edit_event_count.clone(); + cx.subscribe( + &leader_multibuffer, + move |follower, _, event, cx| match event.clone() { + Event::ExcerptsAdded { + buffer, + predecessor, + excerpts, + } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx), + Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx), + Event::Edited { .. } => { + *follower_edit_event_count.write() += 1; + } + _ => {} + }, + ) + .detach(); + }); + + leader_multibuffer.update(cx, |leader, cx| { + leader.push_excerpts( + buffer_1.clone(), + [ + ExcerptRange { + context: 0..8, + primary: None, + }, + ExcerptRange { + context: 12..16, + primary: None, + }, + ], + cx, + ); + leader.insert_excerpts_after( + leader.excerpt_ids()[0], + buffer_2.clone(), + [ + ExcerptRange { + context: 0..5, + primary: None, + }, + ExcerptRange { + context: 10..15, + primary: None, + }, + ], + cx, + ) + }); + assert_eq!( + leader_multibuffer.read(cx).snapshot(cx).text(), + follower_multibuffer.read(cx).snapshot(cx).text(), + ); + assert_eq!(*follower_edit_event_count.read(), 2); + + leader_multibuffer.update(cx, |leader, cx| { + let excerpt_ids = leader.excerpt_ids(); + leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx); + }); + assert_eq!( + leader_multibuffer.read(cx).snapshot(cx).text(), + follower_multibuffer.read(cx).snapshot(cx).text(), + ); + assert_eq!(*follower_edit_event_count.read(), 3); + + // Removing an empty set of excerpts is a noop. + leader_multibuffer.update(cx, |leader, cx| { + leader.remove_excerpts([], cx); + }); + assert_eq!( + leader_multibuffer.read(cx).snapshot(cx).text(), + follower_multibuffer.read(cx).snapshot(cx).text(), + ); + assert_eq!(*follower_edit_event_count.read(), 3); + + // Adding an empty set of excerpts is a noop. + leader_multibuffer.update(cx, |leader, cx| { + leader.push_excerpts::(buffer_2.clone(), [], cx); + }); + assert_eq!( + leader_multibuffer.read(cx).snapshot(cx).text(), + follower_multibuffer.read(cx).snapshot(cx).text(), + ); + assert_eq!(*follower_edit_event_count.read(), 3); + + leader_multibuffer.update(cx, |leader, cx| { + leader.clear(cx); + }); + assert_eq!( + leader_multibuffer.read(cx).snapshot(cx).text(), + follower_multibuffer.read(cx).snapshot(cx).text(), + ); + assert_eq!(*follower_edit_event_count.read(), 4); + } + + #[gpui2::test] + fn test_push_excerpts_with_context_lines(cx: &mut AppContext) { + let buffer = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a'))); + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.push_excerpts_with_context_lines( + buffer.clone(), + vec![ + Point::new(3, 2)..Point::new(4, 2), + Point::new(7, 1)..Point::new(7, 3), + Point::new(15, 0)..Point::new(15, 0), + ], + 2, + cx, + ) + }); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!( + snapshot.text(), + "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n" + ); + + assert_eq!( + anchor_ranges + .iter() + .map(|range| range.to_point(&snapshot)) + .collect::>(), + vec![ + Point::new(2, 2)..Point::new(3, 2), + Point::new(6, 1)..Point::new(6, 3), + Point::new(12, 0)..Point::new(12, 0) + ] + ); + } + + #[gpui2::test] + async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) { + let buffer = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a'))); + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { + let snapshot = buffer.read(cx); + let ranges = vec![ + snapshot.anchor_before(Point::new(3, 2))..snapshot.anchor_before(Point::new(4, 2)), + snapshot.anchor_before(Point::new(7, 1))..snapshot.anchor_before(Point::new(7, 3)), + snapshot.anchor_before(Point::new(15, 0)) + ..snapshot.anchor_before(Point::new(15, 0)), + ]; + multibuffer.stream_excerpts_with_context_lines(buffer.clone(), ranges, 2, cx) + }); + + let anchor_ranges = anchor_ranges.collect::>().await; + + let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx)); + assert_eq!( + snapshot.text(), + "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n" + ); + + assert_eq!( + anchor_ranges + .iter() + .map(|range| range.to_point(&snapshot)) + .collect::>(), + vec![ + Point::new(2, 2)..Point::new(3, 2), + Point::new(6, 1)..Point::new(6, 3), + Point::new(12, 0)..Point::new(12, 0) + ] + ); + } + + #[gpui2::test] + fn test_empty_multibuffer(cx: &mut AppContext) { + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot.text(), ""); + assert_eq!(snapshot.buffer_rows(0).collect::>(), &[Some(0)]); + assert_eq!(snapshot.buffer_rows(1).collect::>(), &[]); + } + + #[gpui2::test] + fn test_singleton_multibuffer_anchors(cx: &mut AppContext) { + let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd")); + let multibuffer = cx.build_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); + let old_snapshot = multibuffer.read(cx).snapshot(cx); + buffer.update(cx, |buffer, cx| { + buffer.edit([(0..0, "X")], None, cx); + buffer.edit([(5..5, "Y")], None, cx); + }); + let new_snapshot = multibuffer.read(cx).snapshot(cx); + + assert_eq!(old_snapshot.text(), "abcd"); + assert_eq!(new_snapshot.text(), "XabcdY"); + + assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0); + assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1); + assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5); + assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6); + } + + #[gpui2::test] + fn test_multibuffer_anchors(cx: &mut AppContext) { + let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd")); + let buffer_2 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi")); + let multibuffer = cx.build_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: 0..4, + primary: None, + }], + cx, + ); + multibuffer.push_excerpts( + buffer_2.clone(), + [ExcerptRange { + context: 0..5, + primary: None, + }], + cx, + ); + multibuffer + }); + let old_snapshot = multibuffer.read(cx).snapshot(cx); + + assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0); + assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0); + assert_eq!(Anchor::min().to_offset(&old_snapshot), 0); + assert_eq!(Anchor::min().to_offset(&old_snapshot), 0); + assert_eq!(Anchor::max().to_offset(&old_snapshot), 10); + assert_eq!(Anchor::max().to_offset(&old_snapshot), 10); + + buffer_1.update(cx, |buffer, cx| { + buffer.edit([(0..0, "W")], None, cx); + buffer.edit([(5..5, "X")], None, cx); + }); + buffer_2.update(cx, |buffer, cx| { + buffer.edit([(0..0, "Y")], None, cx); + buffer.edit([(6..6, "Z")], None, cx); + }); + let new_snapshot = multibuffer.read(cx).snapshot(cx); + + assert_eq!(old_snapshot.text(), "abcd\nefghi"); + assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ"); + + assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0); + assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1); + assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2); + assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2); + assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3); + assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3); + assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7); + assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8); + assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13); + assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14); + } + + #[gpui2::test] + fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) { + let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd")); + let buffer_2 = + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP")); + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + + // Create an insertion id in buffer 1 that doesn't exist in buffer 2. + // Add an excerpt from buffer 1 that spans this new insertion. + buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx)); + let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| { + multibuffer + .push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: 0..7, + primary: None, + }], + cx, + ) + .pop() + .unwrap() + }); + + let snapshot_1 = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot_1.text(), "abcd123"); + + // Replace the buffer 1 excerpt with new excerpts from buffer 2. + let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.remove_excerpts([excerpt_id_1], cx); + let mut ids = multibuffer + .push_excerpts( + buffer_2.clone(), + [ + ExcerptRange { + context: 0..4, + primary: None, + }, + ExcerptRange { + context: 6..10, + primary: None, + }, + ExcerptRange { + context: 12..16, + primary: None, + }, + ], + cx, + ) + .into_iter(); + (ids.next().unwrap(), ids.next().unwrap()) + }); + let snapshot_2 = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot_2.text(), "ABCD\nGHIJ\nMNOP"); + + // The old excerpt id doesn't get reused. + assert_ne!(excerpt_id_2, excerpt_id_1); + + // Resolve some anchors from the previous snapshot in the new snapshot. + // The current excerpts are from a different buffer, so we don't attempt to + // resolve the old text anchor in the new buffer. + assert_eq!( + snapshot_2.summary_for_anchor::(&snapshot_1.anchor_before(2)), + 0 + ); + assert_eq!( + snapshot_2.summaries_for_anchors::(&[ + snapshot_1.anchor_before(2), + snapshot_1.anchor_after(3) + ]), + vec![0, 0] + ); + + // Refresh anchors from the old snapshot. The return value indicates that both + // anchors lost their original excerpt. + let refresh = + snapshot_2.refresh_anchors(&[snapshot_1.anchor_before(2), snapshot_1.anchor_after(3)]); + assert_eq!( + refresh, + &[ + (0, snapshot_2.anchor_before(0), false), + (1, snapshot_2.anchor_after(0), false), + ] + ); + + // Replace the middle excerpt with a smaller excerpt in buffer 2, + // that intersects the old excerpt. + let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.remove_excerpts([excerpt_id_3], cx); + multibuffer + .insert_excerpts_after( + excerpt_id_2, + buffer_2.clone(), + [ExcerptRange { + context: 5..8, + primary: None, + }], + cx, + ) + .pop() + .unwrap() + }); + + let snapshot_3 = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot_3.text(), "ABCD\nFGH\nMNOP"); + assert_ne!(excerpt_id_5, excerpt_id_3); + + // Resolve some anchors from the previous snapshot in the new snapshot. + // The third anchor can't be resolved, since its excerpt has been removed, + // so it resolves to the same position as its predecessor. + let anchors = [ + snapshot_2.anchor_before(0), + snapshot_2.anchor_after(2), + snapshot_2.anchor_after(6), + snapshot_2.anchor_after(14), + ]; + assert_eq!( + snapshot_3.summaries_for_anchors::(&anchors), + &[0, 2, 9, 13] + ); + + let new_anchors = snapshot_3.refresh_anchors(&anchors); + assert_eq!( + new_anchors.iter().map(|a| (a.0, a.2)).collect::>(), + &[(0, true), (1, true), (2, true), (3, true)] + ); + assert_eq!( + snapshot_3.summaries_for_anchors::(new_anchors.iter().map(|a| &a.1)), + &[0, 2, 7, 13] + ); + } + + #[gpui2::test(iterations = 100)] + fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) { + let operations = env::var("OPERATIONS") + .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) + .unwrap_or(10); + + let mut buffers: Vec> = Vec::new(); + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + let mut excerpt_ids = Vec::::new(); + let mut expected_excerpts = Vec::<(Model, Range)>::new(); + let mut anchors = Vec::new(); + let mut old_versions = Vec::new(); + + for _ in 0..operations { + match rng.gen_range(0..100) { + 0..=19 if !buffers.is_empty() => { + let buffer = buffers.choose(&mut rng).unwrap(); + buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx)); + } + 20..=29 if !expected_excerpts.is_empty() => { + let mut ids_to_remove = vec![]; + for _ in 0..rng.gen_range(1..=3) { + if expected_excerpts.is_empty() { + break; + } + + let ix = rng.gen_range(0..expected_excerpts.len()); + ids_to_remove.push(excerpt_ids.remove(ix)); + let (buffer, range) = expected_excerpts.remove(ix); + let buffer = buffer.read(cx); + log::info!( + "Removing excerpt {}: {:?}", + ix, + buffer + .text_for_range(range.to_offset(buffer)) + .collect::(), + ); + } + let snapshot = multibuffer.read(cx).read(cx); + ids_to_remove.sort_unstable_by(|a, b| a.cmp(&b, &snapshot)); + drop(snapshot); + multibuffer.update(cx, |multibuffer, cx| { + multibuffer.remove_excerpts(ids_to_remove, cx) + }); + } + 30..=39 if !expected_excerpts.is_empty() => { + let multibuffer = multibuffer.read(cx).read(cx); + let offset = + multibuffer.clip_offset(rng.gen_range(0..=multibuffer.len()), Bias::Left); + let bias = if rng.gen() { Bias::Left } else { Bias::Right }; + log::info!("Creating anchor at {} with bias {:?}", offset, bias); + anchors.push(multibuffer.anchor_at(offset, bias)); + anchors.sort_by(|a, b| a.cmp(b, &multibuffer)); + } + 40..=44 if !anchors.is_empty() => { + let multibuffer = multibuffer.read(cx).read(cx); + let prev_len = anchors.len(); + anchors = multibuffer + .refresh_anchors(&anchors) + .into_iter() + .map(|a| a.1) + .collect(); + + // Ensure the newly-refreshed anchors point to a valid excerpt and don't + // overshoot its boundaries. + assert_eq!(anchors.len(), prev_len); + for anchor in &anchors { + if anchor.excerpt_id == ExcerptId::min() + || anchor.excerpt_id == ExcerptId::max() + { + continue; + } + + let excerpt = multibuffer.excerpt(anchor.excerpt_id).unwrap(); + assert_eq!(excerpt.id, anchor.excerpt_id); + assert!(excerpt.contains(anchor)); + } + } + _ => { + let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) { + let base_text = util::RandomCharIter::new(&mut rng) + .take(10) + .collect::(); + buffers.push( + cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text)), + ); + buffers.last().unwrap() + } else { + buffers.choose(&mut rng).unwrap() + }; + + let buffer = buffer_handle.read(cx); + let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right); + let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); + let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix); + let prev_excerpt_ix = rng.gen_range(0..=expected_excerpts.len()); + let prev_excerpt_id = excerpt_ids + .get(prev_excerpt_ix) + .cloned() + .unwrap_or_else(ExcerptId::max); + let excerpt_ix = (prev_excerpt_ix + 1).min(expected_excerpts.len()); + + log::info!( + "Inserting excerpt at {} of {} for buffer {}: {:?}[{:?}] = {:?}", + excerpt_ix, + expected_excerpts.len(), + buffer_handle.read(cx).remote_id(), + buffer.text(), + start_ix..end_ix, + &buffer.text()[start_ix..end_ix] + ); + + let excerpt_id = multibuffer.update(cx, |multibuffer, cx| { + multibuffer + .insert_excerpts_after( + prev_excerpt_id, + buffer_handle.clone(), + [ExcerptRange { + context: start_ix..end_ix, + primary: None, + }], + cx, + ) + .pop() + .unwrap() + }); + + excerpt_ids.insert(excerpt_ix, excerpt_id); + expected_excerpts.insert(excerpt_ix, (buffer_handle.clone(), anchor_range)); + } + } + + if rng.gen_bool(0.3) { + multibuffer.update(cx, |multibuffer, cx| { + old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe())); + }) + } + + let snapshot = multibuffer.read(cx).snapshot(cx); + + let mut excerpt_starts = Vec::new(); + let mut expected_text = String::new(); + let mut expected_buffer_rows = Vec::new(); + for (buffer, range) in &expected_excerpts { + let buffer = buffer.read(cx); + let buffer_range = range.to_offset(buffer); + + excerpt_starts.push(TextSummary::from(expected_text.as_str())); + expected_text.extend(buffer.text_for_range(buffer_range.clone())); + expected_text.push('\n'); + + let buffer_row_range = buffer.offset_to_point(buffer_range.start).row + ..=buffer.offset_to_point(buffer_range.end).row; + for row in buffer_row_range { + expected_buffer_rows.push(Some(row)); + } + } + // Remove final trailing newline. + if !expected_excerpts.is_empty() { + expected_text.pop(); + } + + // Always report one buffer row + if expected_buffer_rows.is_empty() { + expected_buffer_rows.push(Some(0)); + } + + assert_eq!(snapshot.text(), expected_text); + log::info!("MultiBuffer text: {:?}", expected_text); + + assert_eq!( + snapshot.buffer_rows(0).collect::>(), + expected_buffer_rows, + ); + + for _ in 0..5 { + let start_row = rng.gen_range(0..=expected_buffer_rows.len()); + assert_eq!( + snapshot.buffer_rows(start_row as u32).collect::>(), + &expected_buffer_rows[start_row..], + "buffer_rows({})", + start_row + ); + } + + assert_eq!( + snapshot.max_buffer_row(), + expected_buffer_rows.into_iter().flatten().max().unwrap() + ); + + let mut excerpt_starts = excerpt_starts.into_iter(); + for (buffer, range) in &expected_excerpts { + let buffer = buffer.read(cx); + let buffer_id = buffer.remote_id(); + let buffer_range = range.to_offset(buffer); + let buffer_start_point = buffer.offset_to_point(buffer_range.start); + let buffer_start_point_utf16 = + buffer.text_summary_for_range::(0..buffer_range.start); + + let excerpt_start = excerpt_starts.next().unwrap(); + let mut offset = excerpt_start.len; + let mut buffer_offset = buffer_range.start; + let mut point = excerpt_start.lines; + let mut buffer_point = buffer_start_point; + let mut point_utf16 = excerpt_start.lines_utf16(); + let mut buffer_point_utf16 = buffer_start_point_utf16; + for ch in buffer + .snapshot() + .chunks(buffer_range.clone(), false) + .flat_map(|c| c.text.chars()) + { + for _ in 0..ch.len_utf8() { + let left_offset = snapshot.clip_offset(offset, Bias::Left); + let right_offset = snapshot.clip_offset(offset, Bias::Right); + let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left); + let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right); + assert_eq!( + left_offset, + excerpt_start.len + (buffer_left_offset - buffer_range.start), + "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}", + offset, + buffer_id, + buffer_offset, + ); + assert_eq!( + right_offset, + excerpt_start.len + (buffer_right_offset - buffer_range.start), + "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}", + offset, + buffer_id, + buffer_offset, + ); + + let left_point = snapshot.clip_point(point, Bias::Left); + let right_point = snapshot.clip_point(point, Bias::Right); + let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left); + let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right); + assert_eq!( + left_point, + excerpt_start.lines + (buffer_left_point - buffer_start_point), + "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}", + point, + buffer_id, + buffer_point, + ); + assert_eq!( + right_point, + excerpt_start.lines + (buffer_right_point - buffer_start_point), + "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}", + point, + buffer_id, + buffer_point, + ); + + assert_eq!( + snapshot.point_to_offset(left_point), + left_offset, + "point_to_offset({:?})", + left_point, + ); + assert_eq!( + snapshot.offset_to_point(left_offset), + left_point, + "offset_to_point({:?})", + left_offset, + ); + + offset += 1; + buffer_offset += 1; + if ch == '\n' { + point += Point::new(1, 0); + buffer_point += Point::new(1, 0); + } else { + point += Point::new(0, 1); + buffer_point += Point::new(0, 1); + } + } + + for _ in 0..ch.len_utf16() { + let left_point_utf16 = + snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Left); + let right_point_utf16 = + snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Right); + let buffer_left_point_utf16 = + buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Left); + let buffer_right_point_utf16 = + buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Right); + assert_eq!( + left_point_utf16, + excerpt_start.lines_utf16() + + (buffer_left_point_utf16 - buffer_start_point_utf16), + "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}", + point_utf16, + buffer_id, + buffer_point_utf16, + ); + assert_eq!( + right_point_utf16, + excerpt_start.lines_utf16() + + (buffer_right_point_utf16 - buffer_start_point_utf16), + "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}", + point_utf16, + buffer_id, + buffer_point_utf16, + ); + + if ch == '\n' { + point_utf16 += PointUtf16::new(1, 0); + buffer_point_utf16 += PointUtf16::new(1, 0); + } else { + point_utf16 += PointUtf16::new(0, 1); + buffer_point_utf16 += PointUtf16::new(0, 1); + } + } + } + } + + for (row, line) in expected_text.split('\n').enumerate() { + assert_eq!( + snapshot.line_len(row as u32), + line.len() as u32, + "line_len({}).", + row + ); + } + + let text_rope = Rope::from(expected_text.as_str()); + for _ in 0..10 { + let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right); + let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); + + let text_for_range = snapshot + .text_for_range(start_ix..end_ix) + .collect::(); + assert_eq!( + text_for_range, + &expected_text[start_ix..end_ix], + "incorrect text for range {:?}", + start_ix..end_ix + ); + + let excerpted_buffer_ranges = multibuffer + .read(cx) + .range_to_buffer_ranges(start_ix..end_ix, cx); + let excerpted_buffers_text = excerpted_buffer_ranges + .iter() + .map(|(buffer, buffer_range, _)| { + buffer + .read(cx) + .text_for_range(buffer_range.clone()) + .collect::() + }) + .collect::>() + .join("\n"); + assert_eq!(excerpted_buffers_text, text_for_range); + if !expected_excerpts.is_empty() { + assert!(!excerpted_buffer_ranges.is_empty()); + } + + let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]); + assert_eq!( + snapshot.text_summary_for_range::(start_ix..end_ix), + expected_summary, + "incorrect summary for range {:?}", + start_ix..end_ix + ); + } + + // Anchor resolution + let summaries = snapshot.summaries_for_anchors::(&anchors); + assert_eq!(anchors.len(), summaries.len()); + for (anchor, resolved_offset) in anchors.iter().zip(summaries) { + assert!(resolved_offset <= snapshot.len()); + assert_eq!( + snapshot.summary_for_anchor::(anchor), + resolved_offset + ); + } + + for _ in 0..10 { + let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right); + assert_eq!( + snapshot.reversed_chars_at(end_ix).collect::(), + expected_text[..end_ix].chars().rev().collect::(), + ); + } + + for _ in 0..10 { + let end_ix = rng.gen_range(0..=text_rope.len()); + let start_ix = rng.gen_range(0..=end_ix); + assert_eq!( + snapshot + .bytes_in_range(start_ix..end_ix) + .flatten() + .copied() + .collect::>(), + expected_text.as_bytes()[start_ix..end_ix].to_vec(), + "bytes_in_range({:?})", + start_ix..end_ix, + ); + } + } + + let snapshot = multibuffer.read(cx).snapshot(cx); + for (old_snapshot, subscription) in old_versions { + let edits = subscription.consume().into_inner(); + + log::info!( + "applying subscription edits to old text: {:?}: {:?}", + old_snapshot.text(), + edits, + ); + + let mut text = old_snapshot.text(); + for edit in edits { + let new_text: String = snapshot.text_for_range(edit.new.clone()).collect(); + text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text); + } + assert_eq!(text.to_string(), snapshot.text()); + } + } + + #[gpui2::test] + fn test_history(cx: &mut AppContext) { + let test_settings = SettingsStore::test(cx); + cx.set_global(test_settings); + + let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "1234")); + let buffer_2 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "5678")); + let multibuffer = cx.build_model(|_| MultiBuffer::new(0)); + let group_interval = multibuffer.read(cx).history.group_interval; + multibuffer.update(cx, |multibuffer, cx| { + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: 0..buffer_1.read(cx).len(), + primary: None, + }], + cx, + ); + multibuffer.push_excerpts( + buffer_2.clone(), + [ExcerptRange { + context: 0..buffer_2.read(cx).len(), + primary: None, + }], + cx, + ); + }); + + let mut now = Instant::now(); + + multibuffer.update(cx, |multibuffer, cx| { + let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap(); + multibuffer.edit( + [ + (Point::new(0, 0)..Point::new(0, 0), "A"), + (Point::new(1, 0)..Point::new(1, 0), "A"), + ], + None, + cx, + ); + multibuffer.edit( + [ + (Point::new(0, 1)..Point::new(0, 1), "B"), + (Point::new(1, 1)..Point::new(1, 1), "B"), + ], + None, + cx, + ); + multibuffer.end_transaction_at(now, cx); + assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678"); + + // Edit buffer 1 through the multibuffer + now += 2 * group_interval; + multibuffer.start_transaction_at(now, cx); + multibuffer.edit([(2..2, "C")], None, cx); + multibuffer.end_transaction_at(now, cx); + assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678"); + + // Edit buffer 1 independently + buffer_1.update(cx, |buffer_1, cx| { + buffer_1.start_transaction_at(now); + buffer_1.edit([(3..3, "D")], None, cx); + buffer_1.end_transaction_at(now, cx); + + now += 2 * group_interval; + buffer_1.start_transaction_at(now); + buffer_1.edit([(4..4, "E")], None, cx); + buffer_1.end_transaction_at(now, cx); + }); + assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678"); + + // An undo in the multibuffer undoes the multibuffer transaction + // and also any individual buffer edits that have occurred since + // that transaction. + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678"); + + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "1234\n5678"); + + multibuffer.redo(cx); + assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678"); + + multibuffer.redo(cx); + assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678"); + + // Undo buffer 2 independently. + buffer_2.update(cx, |buffer_2, cx| buffer_2.undo(cx)); + assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\n5678"); + + // An undo in the multibuffer undoes the components of the + // the last multibuffer transaction that are not already undone. + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "AB1234\n5678"); + + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "1234\n5678"); + + multibuffer.redo(cx); + assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678"); + + buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx)); + assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678"); + + // Redo stack gets cleared after an edit. + now += 2 * group_interval; + multibuffer.start_transaction_at(now, cx); + multibuffer.edit([(0..0, "X")], None, cx); + multibuffer.end_transaction_at(now, cx); + assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678"); + multibuffer.redo(cx); + assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678"); + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678"); + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "1234\n5678"); + + // Transactions can be grouped manually. + multibuffer.redo(cx); + multibuffer.redo(cx); + assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678"); + multibuffer.group_until_transaction(transaction_1, cx); + multibuffer.undo(cx); + assert_eq!(multibuffer.read(cx).text(), "1234\n5678"); + multibuffer.redo(cx); + assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678"); + }); + } +} From 18431051d9d750d9e66284a71f7a55a1e31c1374 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 03:23:00 +0100 Subject: [PATCH 10/18] Rework `theme2` with new theme structure (#3194) This PR reworks the theme definition in the `theme2` crate to be based off of the new theme work that @iamnbutler has been working on. We're still developing the new theme system, but it is complete enough that we can now load the default theme and use it to theme the storybook (albeit with some further refining of the color palette required). --------- Co-authored-by: Nate Butler Co-authored-by: Marshall Bowers --- Cargo.lock | 1 + crates/storybook2/src/stories/colors.rs | 7 +- crates/storybook2/src/stories/focus.rs | 16 +- crates/storybook2/src/stories/scroll.rs | 10 +- crates/storybook2/src/storybook2.rs | 3 +- crates/theme2/Cargo.toml | 12 +- crates/theme2/src/colors.rs | 143 +++ .../src/{default.rs => default_colors.rs} | 838 ++++++++++++++++-- crates/theme2/src/default_theme.rs | 58 ++ crates/theme2/src/scale.rs | 193 ++-- crates/theme2/src/settings.rs | 10 +- crates/theme2/src/syntax.rs | 227 +++++ crates/theme2/src/theme2.rs | 63 +- crates/theme2/src/utils.rs | 43 + crates/ui2/src/components/breadcrumb.rs | 24 +- crates/ui2/src/components/buffer.rs | 19 +- crates/ui2/src/components/buffer_search.rs | 4 +- crates/ui2/src/components/collab_panel.rs | 30 +- crates/ui2/src/components/context_menu.rs | 6 +- crates/ui2/src/components/icon_button.rs | 14 +- crates/ui2/src/components/keybinding.rs | 6 +- crates/ui2/src/components/list.rs | 34 +- crates/ui2/src/components/modal.rs | 10 +- crates/ui2/src/components/multi_buffer.rs | 16 +- .../ui2/src/components/notification_toast.rs | 7 +- .../ui2/src/components/notifications_panel.rs | 4 +- crates/ui2/src/components/palette.rs | 16 +- crates/ui2/src/components/panel.rs | 6 +- crates/ui2/src/components/panes.rs | 4 +- crates/ui2/src/components/player_stack.rs | 4 +- crates/ui2/src/components/project_panel.rs | 4 +- crates/ui2/src/components/status_bar.rs | 4 +- crates/ui2/src/components/tab.rs | 13 +- crates/ui2/src/components/tab_bar.rs | 4 +- crates/ui2/src/components/terminal.rs | 6 +- crates/ui2/src/components/title_bar.rs | 3 +- crates/ui2/src/components/toast.rs | 4 +- crates/ui2/src/components/toolbar.rs | 14 +- crates/ui2/src/components/traffic_lights.rs | 10 +- crates/ui2/src/components/workspace.rs | 8 +- crates/ui2/src/elements/avatar.rs | 5 +- crates/ui2/src/elements/button.rs | 18 +- crates/ui2/src/elements/details.rs | 7 +- crates/ui2/src/elements/icon.rs | 13 +- crates/ui2/src/elements/input.rs | 16 +- crates/ui2/src/elements/label.rs | 16 +- crates/ui2/src/elements/player.rs | 8 +- crates/ui2/src/elements/tool_divider.rs | 4 +- crates/ui2/src/prelude.rs | 16 +- crates/ui2/src/static_data.rs | 96 +- crates/ui2/src/story.rs | 12 +- 51 files changed, 1615 insertions(+), 494 deletions(-) create mode 100644 crates/theme2/src/colors.rs rename crates/theme2/src/{default.rs => default_colors.rs} (65%) create mode 100644 crates/theme2/src/default_theme.rs create mode 100644 crates/theme2/src/syntax.rs create mode 100644 crates/theme2/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 3aca27106c..272320895d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8759,6 +8759,7 @@ dependencies = [ "gpui2", "indexmap 1.9.3", "parking_lot 0.11.2", + "refineable", "schemars", "serde", "serde_derive", diff --git a/crates/storybook2/src/stories/colors.rs b/crates/storybook2/src/stories/colors.rs index afc29660ff..0dd56071c8 100644 --- a/crates/storybook2/src/stories/colors.rs +++ b/crates/storybook2/src/stories/colors.rs @@ -1,5 +1,6 @@ use crate::story::Story; use gpui2::{px, Div, Render}; +use theme2::default_color_scales; use ui::prelude::*; pub struct ColorsStory; @@ -8,7 +9,7 @@ impl Render for ColorsStory { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - let color_scales = theme2::default_color_scales(); + let color_scales = default_color_scales(); Story::container(cx) .child(Story::title(cx, "Colors")) @@ -20,14 +21,14 @@ impl Render for ColorsStory { .gap_1() .overflow_y_scroll() .text_color(gpui2::white()) - .children(color_scales.into_iter().map(|(name, scale)| { + .children(color_scales.into_iter().map(|scale| { div() .flex() .child( div() .w(px(75.)) .line_height(px(24.)) - .child(name.to_string()), + .child(scale.name().to_string()), ) .child(div().flex().gap_1().children( (1..=12).map(|step| div().flex().size_6().bg(scale.step(cx, step))), diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index f3f6a8d5fb..aa71040b47 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/crates/storybook2/src/stories/focus.rs @@ -3,7 +3,7 @@ use gpui2::{ StatelessInteractive, Styled, View, VisualContext, WindowContext, }; use serde::Deserialize; -use theme2::theme; +use theme2::ActiveTheme; #[derive(Clone, Default, PartialEq, Deserialize)] struct ActionA; @@ -34,13 +34,13 @@ 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; - let color_4 = theme.git_conflict; - let color_5 = theme.git_ignored; - let color_6 = theme.git_renamed; + let theme = cx.theme(); + let color_1 = theme.styles.git.created; + let color_2 = theme.styles.git.modified; + let color_3 = theme.styles.git.deleted; + let color_4 = theme.styles.git.conflict; + let color_5 = theme.styles.git.ignored; + let color_6 = theme.styles.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 b504a512a6..9236629c34 100644 --- a/crates/storybook2/src/stories/scroll.rs +++ b/crates/storybook2/src/stories/scroll.rs @@ -2,7 +2,7 @@ use gpui2::{ div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled, View, VisualContext, WindowContext, }; -use theme2::theme; +use theme2::ActiveTheme; pub struct ScrollStory; @@ -16,13 +16,13 @@ impl Render for ScrollStory { type Element = Div>; 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 theme = cx.theme(); + let color_1 = theme.styles.git.created; + let color_2 = theme.styles.git.modified; div() .id("parent") - .bg(theme.background) + .bg(theme.colors().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 c2903c88e1..6028695d7f 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -68,10 +68,9 @@ fn main() { let theme_registry = cx.global::(); let mut theme_settings = ThemeSettings::get_global(cx).clone(); - theme_settings.active_theme = theme_registry.get(&theme_name).unwrap(); + theme_settings.old_active_theme = theme_registry.get(&theme_name).unwrap(); ThemeSettings::override_global(theme_settings, cx); - cx.set_global(theme.clone()); ui::settings::init(cx); let window = cx.open_window( diff --git a/crates/theme2/Cargo.toml b/crates/theme2/Cargo.toml index 2f89425d21..6b273e5042 100644 --- a/crates/theme2/Cargo.toml +++ b/crates/theme2/Cargo.toml @@ -16,19 +16,19 @@ path = "src/theme2.rs" doctest = false [dependencies] -gpui2 = { path = "../gpui2" } -fs = { path = "../fs" } -schemars.workspace = true -settings2 = { path = "../settings2" } -util = { path = "../util" } - anyhow.workspace = true +fs = { path = "../fs" } +gpui2 = { path = "../gpui2" } indexmap = "1.6.2" parking_lot.workspace = true +refineable.workspace = true +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true +settings2 = { path = "../settings2" } toml.workspace = true +util = { path = "../util" } [dev-dependencies] gpui2 = { path = "../gpui2", features = ["test-support"] } diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs new file mode 100644 index 0000000000..d23fde1ee0 --- /dev/null +++ b/crates/theme2/src/colors.rs @@ -0,0 +1,143 @@ +use gpui2::Hsla; +use refineable::Refineable; + +use crate::{generate_struct_with_overrides, SyntaxStyles}; + +pub struct SystemColors { + pub transparent: Hsla, + pub mac_os_traffic_light_red: Hsla, + pub mac_os_traffic_light_yellow: Hsla, + pub mac_os_traffic_light_green: Hsla, +} + +#[derive(Debug, Clone, Copy)] +pub struct PlayerColor { + pub cursor: Hsla, + pub background: Hsla, + pub selection: Hsla, +} + +pub struct PlayerColors(pub Vec); + +#[derive(Refineable, Clone, Debug)] +#[refineable(debug)] +pub struct StatusColors { + pub conflict: Hsla, + pub created: Hsla, + pub deleted: Hsla, + pub error: Hsla, + pub hidden: Hsla, + pub ignored: Hsla, + pub info: Hsla, + pub modified: Hsla, + pub renamed: Hsla, + pub success: Hsla, + pub warning: Hsla, +} + +#[derive(Refineable, Clone, Debug)] +#[refineable(debug)] +pub struct GitStatusColors { + pub conflict: Hsla, + pub created: Hsla, + pub deleted: Hsla, + pub ignored: Hsla, + pub modified: Hsla, + pub renamed: Hsla, +} + +#[derive(Refineable, Clone, Debug)] +#[refineable(debug)] +pub struct ThemeColors { + pub border: Hsla, + pub border_variant: Hsla, + pub border_focused: Hsla, + pub border_transparent: Hsla, + pub elevated_surface: Hsla, + pub surface: Hsla, + pub background: Hsla, + pub element: Hsla, + pub element_hover: Hsla, + pub element_active: Hsla, + pub element_selected: Hsla, + pub element_disabled: Hsla, + pub element_placeholder: Hsla, + pub ghost_element: Hsla, + pub ghost_element_hover: Hsla, + pub ghost_element_active: Hsla, + pub ghost_element_selected: Hsla, + pub ghost_element_disabled: Hsla, + pub text: Hsla, + pub text_muted: Hsla, + pub text_placeholder: Hsla, + pub text_disabled: Hsla, + pub text_accent: Hsla, + pub icon: Hsla, + pub icon_muted: Hsla, + pub icon_disabled: Hsla, + pub icon_placeholder: Hsla, + pub icon_accent: Hsla, + pub status_bar: Hsla, + pub title_bar: Hsla, + pub toolbar: Hsla, + pub tab_bar: Hsla, + pub editor: Hsla, + pub editor_subheader: Hsla, + pub editor_active_line: Hsla, +} + +generate_struct_with_overrides! { + ThemeStyle, + ThemeStyleOverrides, + system: SystemColors, + colors: ThemeColors, + status: StatusColors, + git: GitStatusColors, + player: PlayerColors, + syntax: SyntaxStyles +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn override_a_single_theme_color() { + let mut colors = ThemeColors::default_light(); + + let magenta: Hsla = gpui2::rgb(0xff00ff); + + assert_ne!(colors.text, magenta); + + let overrides = ThemeColorsRefinement { + text: Some(magenta), + ..Default::default() + }; + + colors.refine(&overrides); + + assert_eq!(colors.text, magenta); + } + + #[test] + fn override_multiple_theme_colors() { + let mut colors = ThemeColors::default_light(); + + let magenta: Hsla = gpui2::rgb(0xff00ff); + let green: Hsla = gpui2::rgb(0x00ff00); + + assert_ne!(colors.text, magenta); + assert_ne!(colors.background, green); + + let overrides = ThemeColorsRefinement { + text: Some(magenta), + background: Some(green), + ..Default::default() + }; + + colors.refine(&overrides); + + assert_eq!(colors.text, magenta); + assert_eq!(colors.background, green); + } +} diff --git a/crates/theme2/src/default.rs b/crates/theme2/src/default_colors.rs similarity index 65% rename from crates/theme2/src/default.rs rename to crates/theme2/src/default_colors.rs index 41d408f980..e8146cdeaa 100644 --- a/crates/theme2/src/default.rs +++ b/crates/theme2/src/default_colors.rs @@ -1,10 +1,704 @@ -use gpui2::Rgba; +use gpui2::{hsla, FontWeight, Rgba}; use indexmap::IndexMap; -use crate::scale::{ColorScaleName, ColorScaleSet, ColorScales}; +use crate::{ + colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, + scale::{ColorScaleSet, ColorScales}, + syntax::{SyntaxStyleName, SyntaxStyles}, + SyntaxStyle, +}; + +impl Default for SystemColors { + fn default() -> Self { + Self { + transparent: hsla(0.0, 0.0, 0.0, 0.0), + mac_os_traffic_light_red: hsla(0.0139, 0.79, 0.65, 1.0), + mac_os_traffic_light_yellow: hsla(0.114, 0.88, 0.63, 1.0), + mac_os_traffic_light_green: hsla(0.313, 0.49, 0.55, 1.0), + } + } +} + +impl Default for StatusColors { + fn default() -> Self { + Self { + conflict: gpui2::black(), + created: gpui2::black(), + deleted: gpui2::black(), + error: gpui2::black(), + hidden: gpui2::black(), + ignored: gpui2::black(), + info: gpui2::black(), + modified: gpui2::black(), + renamed: gpui2::black(), + success: gpui2::black(), + warning: gpui2::black(), + } + } +} + +impl Default for GitStatusColors { + fn default() -> Self { + Self { + conflict: gpui2::rgba(0xdec184ff).into(), + created: gpui2::rgba(0xa1c181ff).into(), + deleted: gpui2::rgba(0xd07277ff).into(), + ignored: gpui2::rgba(0x555a63ff).into(), + modified: gpui2::rgba(0x74ade8ff).into(), + renamed: gpui2::rgba(0xdec184ff).into(), + } + } +} + +impl Default for PlayerColors { + fn default() -> Self { + Self(vec![ + PlayerColor { + cursor: hsla(0.0, 0.0, 0.0, 0.0), + background: hsla(0.0, 0.0, 0.0, 0.0), + selection: hsla(0.0, 0.0, 0.0, 0.0), + }, + PlayerColor { + cursor: hsla(0.0, 0.0, 0.0, 0.0), + background: hsla(0.0, 0.0, 0.0, 0.0), + selection: hsla(0.0, 0.0, 0.0, 0.0), + }, + PlayerColor { + cursor: hsla(0.0, 0.0, 0.0, 0.0), + background: hsla(0.0, 0.0, 0.0, 0.0), + selection: hsla(0.0, 0.0, 0.0, 0.0), + }, + PlayerColor { + cursor: hsla(0.0, 0.0, 0.0, 0.0), + background: hsla(0.0, 0.0, 0.0, 0.0), + selection: hsla(0.0, 0.0, 0.0, 0.0), + }, + ]) + } +} + +impl SyntaxStyles { + pub fn default_light() -> Self { + use SyntaxStyleName::*; + + let neutral: ColorScaleSet = slate().into(); + + Self(IndexMap::from_iter([ + ( + Comment, + SyntaxStyle::builder().color(neutral.light(11)).build(), + ), + ( + CommentDoc, + SyntaxStyle::builder().color(neutral.light(11)).build(), + ), + ( + Primary, + SyntaxStyle::builder().color(neutral.light(12)).build(), + ), + ( + Predictive, + SyntaxStyle::builder().color(neutral.light(10)).build(), + ), + ( + Hint, + SyntaxStyle::builder() + .color(ColorScaleSet::from(cyan()).light(10)) + .build(), + ), + ( + Emphasis, + SyntaxStyle::builder().weight(FontWeight(600.0)).build(), + ), + ( + EmphasisStrong, + SyntaxStyle::builder().weight(FontWeight(800.0)).build(), + ), + ( + Title, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + LinkUri, + SyntaxStyle::builder() + .color(ColorScaleSet::from(blue()).light(12)) + .build(), + ), + ( + LinkText, + SyntaxStyle::builder() + .color(ColorScaleSet::from(orange()).light(12)) + .build(), + ), + ( + TextLiteral, + SyntaxStyle::builder() + .color(ColorScaleSet::from(purple()).light(12)) + .build(), + ), + ( + Punctuation, + SyntaxStyle::builder().color(neutral.light(10)).build(), + ), + ( + PunctuationBracket, + SyntaxStyle::builder().color(neutral.light(10)).build(), + ), + ( + PunctuationDelimiter, + SyntaxStyle::builder().color(neutral.light(10)).build(), + ), + ( + PunctuationSpecial, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + PunctuationListMarker, + SyntaxStyle::builder() + .color(ColorScaleSet::from(blue()).light(12)) + .build(), + ), + ( + String, + SyntaxStyle::builder() + .color(ColorScaleSet::from(green()).light(12)) + .build(), + ), + ( + StringSpecial, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + StringSpecialSymbol, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + StringEscape, + SyntaxStyle::builder() + .color(ColorScaleSet::from(blue()).light(12)) + .build(), + ), + ( + StringRegex, + SyntaxStyle::builder() + .color(ColorScaleSet::from(orange()).light(12)) + .build(), + ), + ( + Constructor, + SyntaxStyle::builder() + .color(ColorScaleSet::from(purple()).light(12)) + .build(), + ), + // TODO: Continue assigning syntax colors from here + ( + Variant, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Type, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + TypeBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Variable, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + VariableSpecial, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Label, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Tag, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Attribute, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Property, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Constant, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Keyword, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Enum, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Operator, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Number, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Boolean, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + ConstantBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Function, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + FunctionBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + FunctionDefinition, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + FunctionSpecialDefinition, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + FunctionMethod, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + FunctionMethodBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Preproc, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ( + Embedded, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).light(12)) + .build(), + ), + ])) + } + + pub fn default_dark() -> Self { + use SyntaxStyleName::*; + + let neutral: ColorScaleSet = slate().into(); + + Self(IndexMap::from_iter([ + ( + Comment, + SyntaxStyle::builder().color(neutral.dark(11)).build(), + ), + ( + CommentDoc, + SyntaxStyle::builder().color(neutral.dark(11)).build(), + ), + ( + Primary, + SyntaxStyle::builder().color(neutral.dark(12)).build(), + ), + ( + Predictive, + SyntaxStyle::builder().color(neutral.dark(10)).build(), + ), + ( + Hint, + SyntaxStyle::builder() + .color(ColorScaleSet::from(cyan()).dark(10)) + .build(), + ), + ( + Emphasis, + SyntaxStyle::builder().weight(FontWeight(600.0)).build(), + ), + ( + EmphasisStrong, + SyntaxStyle::builder().weight(FontWeight(800.0)).build(), + ), + ( + Title, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + LinkUri, + SyntaxStyle::builder() + .color(ColorScaleSet::from(blue()).dark(12)) + .build(), + ), + ( + LinkText, + SyntaxStyle::builder() + .color(ColorScaleSet::from(orange()).dark(12)) + .build(), + ), + ( + TextLiteral, + SyntaxStyle::builder() + .color(ColorScaleSet::from(purple()).dark(12)) + .build(), + ), + ( + Punctuation, + SyntaxStyle::builder().color(neutral.dark(10)).build(), + ), + ( + PunctuationBracket, + SyntaxStyle::builder().color(neutral.dark(10)).build(), + ), + ( + PunctuationDelimiter, + SyntaxStyle::builder().color(neutral.dark(10)).build(), + ), + ( + PunctuationSpecial, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + PunctuationListMarker, + SyntaxStyle::builder() + .color(ColorScaleSet::from(blue()).dark(12)) + .build(), + ), + ( + String, + SyntaxStyle::builder() + .color(ColorScaleSet::from(green()).dark(12)) + .build(), + ), + ( + StringSpecial, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + StringSpecialSymbol, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + StringEscape, + SyntaxStyle::builder() + .color(ColorScaleSet::from(blue()).dark(12)) + .build(), + ), + ( + StringRegex, + SyntaxStyle::builder() + .color(ColorScaleSet::from(orange()).dark(12)) + .build(), + ), + ( + Constructor, + SyntaxStyle::builder() + .color(ColorScaleSet::from(purple()).dark(12)) + .build(), + ), + // TODO: Continue assigning syntax colors from here + ( + Variant, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Type, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + TypeBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Variable, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + VariableSpecial, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Label, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Tag, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Attribute, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Property, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Constant, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Keyword, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Enum, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Operator, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Number, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Boolean, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + ConstantBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Function, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + FunctionBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + FunctionDefinition, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + FunctionSpecialDefinition, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + FunctionMethod, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + FunctionMethodBuiltin, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Preproc, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ( + Embedded, + SyntaxStyle::builder() + .color(ColorScaleSet::from(red()).dark(12)) + .build(), + ), + ])) + } +} + +impl ThemeColors { + pub fn default_light() -> Self { + Self { + border: gpui2::white(), + border_variant: gpui2::white(), + border_focused: gpui2::white(), + border_transparent: gpui2::white(), + elevated_surface: gpui2::white(), + surface: gpui2::white(), + background: gpui2::white(), + element: gpui2::white(), + element_hover: gpui2::white(), + element_active: gpui2::white(), + element_selected: gpui2::white(), + element_disabled: gpui2::white(), + element_placeholder: gpui2::white(), + ghost_element: gpui2::white(), + ghost_element_hover: gpui2::white(), + ghost_element_active: gpui2::white(), + ghost_element_selected: gpui2::white(), + ghost_element_disabled: gpui2::white(), + text: gpui2::white(), + text_muted: gpui2::white(), + text_placeholder: gpui2::white(), + text_disabled: gpui2::white(), + text_accent: gpui2::white(), + icon: gpui2::white(), + icon_muted: gpui2::white(), + icon_disabled: gpui2::white(), + icon_placeholder: gpui2::white(), + icon_accent: gpui2::white(), + status_bar: gpui2::white(), + title_bar: gpui2::white(), + toolbar: gpui2::white(), + tab_bar: gpui2::white(), + editor: gpui2::white(), + editor_subheader: gpui2::white(), + editor_active_line: gpui2::white(), + } + } + + pub fn default_dark() -> Self { + Self { + border: gpui2::rgba(0x464b57ff).into(), + border_variant: gpui2::rgba(0x464b57ff).into(), + border_focused: gpui2::rgba(0x293b5bff).into(), + border_transparent: gpui2::rgba(0x00000000).into(), + elevated_surface: gpui2::rgba(0x3b414dff).into(), + surface: gpui2::rgba(0x2f343eff).into(), + background: gpui2::rgba(0x3b414dff).into(), + element: gpui2::rgba(0x3b414dff).into(), + element_hover: gpui2::rgba(0xffffff1e).into(), + element_active: gpui2::rgba(0xffffff28).into(), + element_selected: gpui2::rgba(0x18243dff).into(), + element_disabled: gpui2::rgba(0x00000000).into(), + element_placeholder: gpui2::black(), + ghost_element: gpui2::rgba(0x00000000).into(), + ghost_element_hover: gpui2::rgba(0xffffff14).into(), + ghost_element_active: gpui2::rgba(0xffffff1e).into(), + ghost_element_selected: gpui2::rgba(0x18243dff).into(), + ghost_element_disabled: gpui2::rgba(0x00000000).into(), + text: gpui2::rgba(0xc8ccd4ff).into(), + text_muted: gpui2::rgba(0x838994ff).into(), + text_placeholder: gpui2::rgba(0xd07277ff).into(), + text_disabled: gpui2::rgba(0x555a63ff).into(), + text_accent: gpui2::rgba(0x74ade8ff).into(), + icon: gpui2::black(), + icon_muted: gpui2::rgba(0x838994ff).into(), + icon_disabled: gpui2::black(), + icon_placeholder: gpui2::black(), + icon_accent: gpui2::black(), + status_bar: gpui2::rgba(0x3b414dff).into(), + title_bar: gpui2::rgba(0x3b414dff).into(), + toolbar: gpui2::rgba(0x282c33ff).into(), + tab_bar: gpui2::rgba(0x2f343eff).into(), + editor: gpui2::rgba(0x282c33ff).into(), + editor_subheader: gpui2::rgba(0x2f343eff).into(), + editor_active_line: gpui2::rgba(0x2f343eff).into(), + } + } +} struct DefaultColorScaleSet { - scale: ColorScaleName, + scale: &'static str, light: [&'static str; 12], light_alpha: [&'static str; 12], dark: [&'static str; 12], @@ -32,48 +726,46 @@ impl From for ColorScaleSet { } pub fn default_color_scales() -> ColorScales { - use ColorScaleName::*; - - IndexMap::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()), - ]) + ColorScales { + 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 { DefaultColorScaleSet { - scale: ColorScaleName::Gray, + scale: "Gray", light: [ "#fcfcfcff", "#f9f9f9ff", @@ -135,7 +827,7 @@ fn gray() -> DefaultColorScaleSet { fn mauve() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Mauve, + scale: "Mauve", light: [ "#fdfcfdff", "#faf9fbff", @@ -197,7 +889,7 @@ fn mauve() -> DefaultColorScaleSet { fn slate() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Slate, + scale: "Slate", light: [ "#fcfcfdff", "#f9f9fbff", @@ -259,7 +951,7 @@ fn slate() -> DefaultColorScaleSet { fn sage() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Sage, + scale: "Sage", light: [ "#fbfdfcff", "#f7f9f8ff", @@ -321,7 +1013,7 @@ fn sage() -> DefaultColorScaleSet { fn olive() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Olive, + scale: "Olive", light: [ "#fcfdfcff", "#f8faf8ff", @@ -383,7 +1075,7 @@ fn olive() -> DefaultColorScaleSet { fn sand() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Sand, + scale: "Sand", light: [ "#fdfdfcff", "#f9f9f8ff", @@ -445,7 +1137,7 @@ fn sand() -> DefaultColorScaleSet { fn gold() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Gold, + scale: "Gold", light: [ "#fdfdfcff", "#faf9f2ff", @@ -507,7 +1199,7 @@ fn gold() -> DefaultColorScaleSet { fn bronze() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Bronze, + scale: "Bronze", light: [ "#fdfcfcff", "#fdf7f5ff", @@ -569,7 +1261,7 @@ fn bronze() -> DefaultColorScaleSet { fn brown() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Brown, + scale: "Brown", light: [ "#fefdfcff", "#fcf9f6ff", @@ -631,7 +1323,7 @@ fn brown() -> DefaultColorScaleSet { fn yellow() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Yellow, + scale: "Yellow", light: [ "#fdfdf9ff", "#fefce9ff", @@ -693,7 +1385,7 @@ fn yellow() -> DefaultColorScaleSet { fn amber() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Amber, + scale: "Amber", light: [ "#fefdfbff", "#fefbe9ff", @@ -755,7 +1447,7 @@ fn amber() -> DefaultColorScaleSet { fn orange() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Orange, + scale: "Orange", light: [ "#fefcfbff", "#fff7edff", @@ -817,7 +1509,7 @@ fn orange() -> DefaultColorScaleSet { fn tomato() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Tomato, + scale: "Tomato", light: [ "#fffcfcff", "#fff8f7ff", @@ -879,7 +1571,7 @@ fn tomato() -> DefaultColorScaleSet { fn red() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Red, + scale: "Red", light: [ "#fffcfcff", "#fff7f7ff", @@ -941,7 +1633,7 @@ fn red() -> DefaultColorScaleSet { fn ruby() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Ruby, + scale: "Ruby", light: [ "#fffcfdff", "#fff7f8ff", @@ -1003,7 +1695,7 @@ fn ruby() -> DefaultColorScaleSet { fn crimson() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Crimson, + scale: "Crimson", light: [ "#fffcfdff", "#fef7f9ff", @@ -1065,7 +1757,7 @@ fn crimson() -> DefaultColorScaleSet { fn pink() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Pink, + scale: "Pink", light: [ "#fffcfeff", "#fef7fbff", @@ -1127,7 +1819,7 @@ fn pink() -> DefaultColorScaleSet { fn plum() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Plum, + scale: "Plum", light: [ "#fefcffff", "#fdf7fdff", @@ -1189,7 +1881,7 @@ fn plum() -> DefaultColorScaleSet { fn purple() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Purple, + scale: "Purple", light: [ "#fefcfeff", "#fbf7feff", @@ -1251,7 +1943,7 @@ fn purple() -> DefaultColorScaleSet { fn violet() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Violet, + scale: "Violet", light: [ "#fdfcfeff", "#faf8ffff", @@ -1313,7 +2005,7 @@ fn violet() -> DefaultColorScaleSet { fn iris() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Iris, + scale: "Iris", light: [ "#fdfdffff", "#f8f8ffff", @@ -1375,7 +2067,7 @@ fn iris() -> DefaultColorScaleSet { fn indigo() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Indigo, + scale: "Indigo", light: [ "#fdfdfeff", "#f7f9ffff", @@ -1437,7 +2129,7 @@ fn indigo() -> DefaultColorScaleSet { fn blue() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Blue, + scale: "Blue", light: [ "#fbfdffff", "#f4faffff", @@ -1499,7 +2191,7 @@ fn blue() -> DefaultColorScaleSet { fn cyan() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Cyan, + scale: "Cyan", light: [ "#fafdfeff", "#f2fafbff", @@ -1561,7 +2253,7 @@ fn cyan() -> DefaultColorScaleSet { fn teal() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Teal, + scale: "Teal", light: [ "#fafefdff", "#f3fbf9ff", @@ -1623,7 +2315,7 @@ fn teal() -> DefaultColorScaleSet { fn jade() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Jade, + scale: "Jade", light: [ "#fbfefdff", "#f4fbf7ff", @@ -1685,7 +2377,7 @@ fn jade() -> DefaultColorScaleSet { fn green() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Green, + scale: "Green", light: [ "#fbfefcff", "#f4fbf6ff", @@ -1747,7 +2439,7 @@ fn green() -> DefaultColorScaleSet { fn grass() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Grass, + scale: "Grass", light: [ "#fbfefbff", "#f5fbf5ff", @@ -1809,7 +2501,7 @@ fn grass() -> DefaultColorScaleSet { fn lime() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Lime, + scale: "Lime", light: [ "#fcfdfaff", "#f8faf3ff", @@ -1871,7 +2563,7 @@ fn lime() -> DefaultColorScaleSet { fn mint() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Mint, + scale: "Mint", light: [ "#f9fefdff", "#f2fbf9ff", @@ -1933,7 +2625,7 @@ fn mint() -> DefaultColorScaleSet { fn sky() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Sky, + scale: "Sky", light: [ "#f9feffff", "#f1fafdff", @@ -1995,7 +2687,7 @@ fn sky() -> DefaultColorScaleSet { fn black() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::Black, + scale: "Black", light: [ "#0000000d", "#0000001a", @@ -2057,7 +2749,7 @@ fn black() -> DefaultColorScaleSet { fn white() -> DefaultColorScaleSet { DefaultColorScaleSet { - scale: ColorScaleName::White, + scale: "White", light: [ "#ffffff0d", "#ffffff1a", diff --git a/crates/theme2/src/default_theme.rs b/crates/theme2/src/default_theme.rs new file mode 100644 index 0000000000..4b47e403d6 --- /dev/null +++ b/crates/theme2/src/default_theme.rs @@ -0,0 +1,58 @@ +use crate::{ + colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyle}, + default_color_scales, Appearance, SyntaxStyles, ThemeFamily, ThemeVariant, +}; + +fn zed_pro_daylight() -> ThemeVariant { + ThemeVariant { + id: "zed_pro_daylight".to_string(), + name: "Zed Pro Daylight".to_string(), + appearance: Appearance::Light, + styles: ThemeStyle { + system: SystemColors::default(), + colors: ThemeColors::default_light(), + status: StatusColors::default(), + git: GitStatusColors::default(), + player: PlayerColors::default(), + syntax: SyntaxStyles::default_light(), + }, + } +} + +pub(crate) fn zed_pro_moonlight() -> ThemeVariant { + ThemeVariant { + id: "zed_pro_moonlight".to_string(), + name: "Zed Pro Moonlight".to_string(), + appearance: Appearance::Light, + styles: ThemeStyle { + system: SystemColors::default(), + colors: ThemeColors::default_dark(), + status: StatusColors::default(), + git: GitStatusColors::default(), + player: PlayerColors::default(), + syntax: SyntaxStyles::default_dark(), + }, + } +} + +pub fn zed_pro_family() -> ThemeFamily { + ThemeFamily { + id: "zed_pro".to_string(), + name: "Zed Pro".to_string(), + author: "Zed Team".to_string(), + themes: vec![zed_pro_daylight(), zed_pro_moonlight()], + scales: default_color_scales(), + } +} + +impl Default for ThemeFamily { + fn default() -> Self { + zed_pro_family() + } +} + +impl Default for ThemeVariant { + fn default() -> Self { + zed_pro_daylight() + } +} diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs index 22a607bf07..21c8592d81 100644 --- a/crates/theme2/src/scale.rs +++ b/crates/theme2/src/scale.rs @@ -1,98 +1,95 @@ -use gpui2::{AppContext, Hsla}; -use indexmap::IndexMap; +use gpui2::{AppContext, Hsla, SharedString}; -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", - } - ) - } -} +use crate::{ActiveTheme, Appearance}; pub type ColorScale = [Hsla; 12]; -pub type ColorScales = IndexMap; +pub struct ColorScales { + pub gray: ColorScaleSet, + pub mauve: ColorScaleSet, + pub slate: ColorScaleSet, + pub sage: ColorScaleSet, + pub olive: ColorScaleSet, + pub sand: ColorScaleSet, + pub gold: ColorScaleSet, + pub bronze: ColorScaleSet, + pub brown: ColorScaleSet, + pub yellow: ColorScaleSet, + pub amber: ColorScaleSet, + pub orange: ColorScaleSet, + pub tomato: ColorScaleSet, + pub red: ColorScaleSet, + pub ruby: ColorScaleSet, + pub crimson: ColorScaleSet, + pub pink: ColorScaleSet, + pub plum: ColorScaleSet, + pub purple: ColorScaleSet, + pub violet: ColorScaleSet, + pub iris: ColorScaleSet, + pub indigo: ColorScaleSet, + pub blue: ColorScaleSet, + pub cyan: ColorScaleSet, + pub teal: ColorScaleSet, + pub jade: ColorScaleSet, + pub green: ColorScaleSet, + pub grass: ColorScaleSet, + pub lime: ColorScaleSet, + pub mint: ColorScaleSet, + pub sky: ColorScaleSet, + pub black: ColorScaleSet, + pub white: ColorScaleSet, +} + +impl IntoIterator for ColorScales { + type Item = ColorScaleSet; + + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + vec![ + self.gray, + self.mauve, + self.slate, + self.sage, + self.olive, + self.sand, + self.gold, + self.bronze, + self.brown, + self.yellow, + self.amber, + self.orange, + self.tomato, + self.red, + self.ruby, + self.crimson, + self.pink, + self.plum, + self.purple, + self.violet, + self.iris, + self.indigo, + self.blue, + self.cyan, + self.teal, + self.jade, + self.green, + self.grass, + self.lime, + self.mint, + self.sky, + self.black, + self.white, + ] + .into_iter() + } +} /// A one-based step in a [`ColorScale`]. pub type ColorScaleStep = usize; pub struct ColorScaleSet { - name: ColorScaleName, + name: SharedString, light: ColorScale, dark: ColorScale, light_alpha: ColorScale, @@ -101,14 +98,14 @@ pub struct ColorScaleSet { impl ColorScaleSet { pub fn new( - name: ColorScaleName, + name: impl Into, light: ColorScale, light_alpha: ColorScale, dark: ColorScale, dark_alpha: ColorScale, ) -> Self { Self { - name, + name: name.into(), light, light_alpha, dark, @@ -116,8 +113,8 @@ impl ColorScaleSet { } } - pub fn name(&self) -> String { - self.name.to_string() + pub fn name(&self) -> &SharedString { + &self.name } pub fn light(&self, step: ColorScaleStep) -> Hsla { @@ -136,27 +133,15 @@ impl ColorScaleSet { self.dark_alpha[step - 1] } - fn current_appearance(cx: &AppContext) -> Appearance { - let theme = theme(cx); - if theme.metadata.is_light { - Appearance::Light - } else { - Appearance::Dark - } - } - pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla { - let appearance = Self::current_appearance(cx); - - match appearance { + match cx.theme().appearance { Appearance::Light => self.light(step), Appearance::Dark => self.dark(step), } } pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla { - let appearance = Self::current_appearance(cx); - match appearance { + match cx.theme().appearance { Appearance::Light => self.light_alpha(step), Appearance::Dark => self.dark_alpha(step), } diff --git a/crates/theme2/src/settings.rs b/crates/theme2/src/settings.rs index 379b01dd4b..3a61bbbe1e 100644 --- a/crates/theme2/src/settings.rs +++ b/crates/theme2/src/settings.rs @@ -1,4 +1,4 @@ -use crate::{Theme, ThemeRegistry}; +use crate::{zed_pro_moonlight, Theme, ThemeRegistry, ThemeVariant}; use anyhow::Result; use gpui2::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels}; use schemars::{ @@ -20,7 +20,8 @@ pub struct ThemeSettings { pub buffer_font: Font, pub buffer_font_size: Pixels, pub buffer_line_height: BufferLineHeight, - pub active_theme: Arc, + pub active_theme: Arc, + pub old_active_theme: Arc, } #[derive(Default)] @@ -123,7 +124,8 @@ impl settings2::Settings for ThemeSettings { }, buffer_font_size: defaults.buffer_font_size.unwrap().into(), buffer_line_height: defaults.buffer_line_height.unwrap(), - active_theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), + active_theme: Arc::new(zed_pro_moonlight()), + old_active_theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), }; for value in user_values.into_iter().copied().cloned() { @@ -136,7 +138,7 @@ impl settings2::Settings for ThemeSettings { if let Some(value) = &value.theme { if let Some(theme) = themes.get(value).log_err() { - this.active_theme = theme; + this.old_active_theme = theme; } } diff --git a/crates/theme2/src/syntax.rs b/crates/theme2/src/syntax.rs new file mode 100644 index 0000000000..82c4c87796 --- /dev/null +++ b/crates/theme2/src/syntax.rs @@ -0,0 +1,227 @@ +use gpui2::{FontWeight, Hsla, SharedString}; +use indexmap::IndexMap; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum SyntaxStyleName { + Comment, + CommentDoc, + Primary, + Predictive, + Hint, + Emphasis, + EmphasisStrong, + Title, + LinkUri, + LinkText, + TextLiteral, + Punctuation, + PunctuationBracket, + PunctuationDelimiter, + PunctuationSpecial, + PunctuationListMarker, + String, + StringSpecial, + StringSpecialSymbol, + StringEscape, + StringRegex, + Constructor, + Variant, + Type, + TypeBuiltin, + Variable, + VariableSpecial, + Label, + Tag, + Attribute, + Property, + Constant, + Keyword, + Enum, + Operator, + Number, + Boolean, + ConstantBuiltin, + Function, + FunctionBuiltin, + FunctionDefinition, + FunctionSpecialDefinition, + FunctionMethod, + FunctionMethodBuiltin, + Preproc, + Embedded, + Custom(SharedString), +} + +impl std::str::FromStr for SyntaxStyleName { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "attribute" => Self::Attribute, + "boolean" => Self::Boolean, + "comment" => Self::Comment, + "comment.doc" => Self::CommentDoc, + "constant" => Self::Constant, + "constructor" => Self::Constructor, + "embedded" => Self::Embedded, + "emphasis" => Self::Emphasis, + "emphasis.strong" => Self::EmphasisStrong, + "enum" => Self::Enum, + "function" => Self::Function, + "function.builtin" => Self::FunctionBuiltin, + "function.definition" => Self::FunctionDefinition, + "function.special_definition" => Self::FunctionSpecialDefinition, + "function.method" => Self::FunctionMethod, + "function.method_builtin" => Self::FunctionMethodBuiltin, + "hint" => Self::Hint, + "keyword" => Self::Keyword, + "label" => Self::Label, + "link_text" => Self::LinkText, + "link_uri" => Self::LinkUri, + "number" => Self::Number, + "operator" => Self::Operator, + "predictive" => Self::Predictive, + "preproc" => Self::Preproc, + "primary" => Self::Primary, + "property" => Self::Property, + "punctuation" => Self::Punctuation, + "punctuation.bracket" => Self::PunctuationBracket, + "punctuation.delimiter" => Self::PunctuationDelimiter, + "punctuation.list_marker" => Self::PunctuationListMarker, + "punctuation.special" => Self::PunctuationSpecial, + "string" => Self::String, + "string.escape" => Self::StringEscape, + "string.regex" => Self::StringRegex, + "string.special" => Self::StringSpecial, + "string.special.symbol" => Self::StringSpecialSymbol, + "tag" => Self::Tag, + "text.literal" => Self::TextLiteral, + "title" => Self::Title, + "type" => Self::Type, + "type.builtin" => Self::TypeBuiltin, + "variable" => Self::Variable, + "variable.special" => Self::VariableSpecial, + "constant.builtin" => Self::ConstantBuiltin, + "variant" => Self::Variant, + name => Self::Custom(name.to_string().into()), + }) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct SyntaxStyle { + pub color: Hsla, + pub weight: FontWeight, + pub underline: bool, + pub italic: bool, + // Nate: In the future I'd like to enable using background highlights for syntax highlighting + // pub highlight: Hsla, +} + +impl SyntaxStyle { + pub fn builder() -> SyntaxStyleBuilder { + SyntaxStyleBuilder::new() + } +} + +impl Default for SyntaxStyle { + fn default() -> Self { + Self { + color: gpui2::black(), + weight: FontWeight::default(), + italic: false, + underline: false, + } + } +} + +pub struct SyntaxStyleBuilder { + pub color: Hsla, + pub weight: FontWeight, + pub underline: bool, + pub italic: bool, +} + +impl SyntaxStyleBuilder { + pub fn new() -> Self { + SyntaxStyleBuilder { + color: gpui2::black(), + weight: FontWeight::default(), + underline: false, + italic: false, + } + } + + pub fn color(mut self, color: Hsla) -> Self { + self.color = color; + self + } + + pub fn weight(mut self, weight: FontWeight) -> Self { + self.weight = weight; + self + } + + pub fn underline(mut self, underline: bool) -> Self { + self.underline = underline; + self + } + + pub fn italic(mut self, italic: bool) -> Self { + self.italic = italic; + self + } + + pub fn build(self) -> SyntaxStyle { + SyntaxStyle { + color: self.color, + weight: self.weight, + underline: self.underline, + italic: self.italic, + } + } +} + +pub struct SyntaxStyles(pub IndexMap); + +impl SyntaxStyles { + // TOOD: Get this working with `#[cfg(test)]`. Why isn't it? + pub fn new_test(colors: impl IntoIterator) -> Self { + Self(IndexMap::from_iter(colors.into_iter().map( + |(name, color)| { + ( + name.parse().unwrap(), + SyntaxStyle::builder().color(color).build(), + ) + }, + ))) + } + + pub fn get(&self, name: &str) -> SyntaxStyle { + self.0 + .get(&name.parse::().unwrap()) + .cloned() + .unwrap_or_default() + } + + pub fn color(&self, name: &str) -> Hsla { + self.get(name).color + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_syntax_style_name() { + let name = "comment".parse::().unwrap(); + assert_eq!(name, SyntaxStyleName::Comment); + } + + #[test] + fn create_custom_syntax_style_name() { + let name = "custom".parse::().unwrap(); + assert_eq!(name, SyntaxStyleName::Custom("custom".into())); + } +} diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index b96a23c338..ea1ad5b26c 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -1,17 +1,25 @@ -mod default; +mod colors; +mod default_colors; +mod default_theme; mod registry; mod scale; mod settings; +mod syntax; mod themes; +mod utils; -pub use default::*; +pub use colors::*; +pub use default_colors::*; +pub use default_theme::*; pub use registry::*; pub use scale::*; pub use settings::*; +pub use syntax::*; + +use std::sync::Arc; use gpui2::{AppContext, HighlightStyle, Hsla, SharedString}; use settings2::Settings; -use std::sync::Arc; #[derive(Debug, Clone, PartialEq)] pub enum Appearance { @@ -24,12 +32,53 @@ pub fn init(cx: &mut AppContext) { ThemeSettings::register(cx); } -pub fn active_theme<'a>(cx: &'a AppContext) -> &'a Arc { - &ThemeSettings::get_global(cx).active_theme +pub trait ActiveTheme { + fn theme(&self) -> &ThemeVariant; } -pub fn theme(cx: &AppContext) -> Arc { - active_theme(cx).clone() +impl ActiveTheme for AppContext { + fn theme(&self) -> &ThemeVariant { + &ThemeSettings::get_global(self).active_theme + } +} + +pub struct ThemeFamily { + #[allow(dead_code)] + pub(crate) id: String, + pub name: String, + pub author: String, + pub themes: Vec, + pub scales: ColorScales, +} + +impl ThemeFamily {} + +pub struct ThemeVariant { + #[allow(dead_code)] + pub(crate) id: String, + pub name: String, + pub appearance: Appearance, + pub styles: ThemeStyle, +} + +impl ThemeVariant { + /// Returns the [`ThemeColors`] for the theme. + #[inline(always)] + pub fn colors(&self) -> &ThemeColors { + &self.styles.colors + } + + /// Returns the [`SyntaxStyles`] for the theme. + #[inline(always)] + pub fn syntax(&self) -> &SyntaxStyles { + &self.styles.syntax + } + + /// Returns the color for the syntax node with the given name. + #[inline(always)] + pub fn syntax_color(&self, name: &str) -> Hsla { + self.syntax().color(name) + } } pub struct Theme { diff --git a/crates/theme2/src/utils.rs b/crates/theme2/src/utils.rs new file mode 100644 index 0000000000..ccdcde4274 --- /dev/null +++ b/crates/theme2/src/utils.rs @@ -0,0 +1,43 @@ +/// This macro generates a struct and a corresponding struct with optional fields. +/// +/// It takes as input the name of the struct to be generated, the name of the struct with optional fields, +/// and a list of field names along with their types. +/// +/// # Example +/// ``` +/// generate_struct_with_overrides!( +/// MyStruct, +/// MyStructOverride, +/// field1: i32, +/// field2: String +/// ); +/// ``` +/// This will generate the following structs: +/// ``` +/// pub struct MyStruct { +/// pub field1: i32, +/// pub field2: String, +/// } +/// +/// pub struct MyStructOverride { +/// pub field1: Option, +/// pub field2: Option, +/// } +/// ``` +#[macro_export] +macro_rules! generate_struct_with_overrides { + ($struct_name:ident, $struct_override_name:ident, $($field:ident: $type:ty),*) => { + pub struct $struct_name { + $( + pub $field: $type, + )* + } + + #[allow(dead_code)] + pub struct $struct_override_name { + $( + pub $field: Option<$type>, + )* + } + }; +} diff --git a/crates/ui2/src/components/breadcrumb.rs b/crates/ui2/src/components/breadcrumb.rs index 6b2dfe1cce..163dfabfb0 100644 --- a/crates/ui2/src/components/breadcrumb.rs +++ b/crates/ui2/src/components/breadcrumb.rs @@ -19,24 +19,22 @@ impl Breadcrumb { } fn render_separator(&self, cx: &WindowContext) -> Div { - let theme = theme(cx); - - div().child(" › ").text_color(theme.text_muted) + div() + .child(" › ") + .text_color(cx.theme().colors().text_muted) } fn render(self, view_state: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let symbols_len = self.symbols.len(); h_stack() .id("breadcrumb") .px_1() .text_sm() - .text_color(theme.text_muted) + .text_color(cx.theme().colors().text_muted) .rounded_md() - .hover(|style| style.bg(theme.ghost_element_hover)) - .active(|style| style.bg(theme.ghost_element_active)) + .hover(|style| style.bg(cx.theme().colors().ghost_element_hover)) + .active(|style| style.bg(cx.theme().colors().ghost_element_active)) .child(self.path.clone().to_str().unwrap().to_string()) .child(if !self.symbols.is_empty() { self.render_separator(cx) @@ -84,8 +82,6 @@ mod stories { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - let theme = theme(cx); - Story::container(cx) .child(Story::title_for::<_, Breadcrumb>(cx)) .child(Story::label(cx, "Default")) @@ -95,21 +91,21 @@ mod stories { Symbol(vec![ HighlightedText { text: "impl ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "BreadcrumbStory".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, ]), Symbol(vec![ HighlightedText { text: "fn ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "render".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, ]), ], diff --git a/crates/ui2/src/components/buffer.rs b/crates/ui2/src/components/buffer.rs index 33a98b6ea9..2b3db676ce 100644 --- a/crates/ui2/src/components/buffer.rs +++ b/crates/ui2/src/components/buffer.rs @@ -155,18 +155,16 @@ impl Buffer { } fn render_row(row: BufferRow, cx: &WindowContext) -> impl Component { - let theme = theme(cx); - let line_background = if row.current { - theme.editor_active_line + cx.theme().colors().editor_active_line } else { - theme.transparent + cx.theme().styles.system.transparent }; let line_number_color = if row.current { - theme.text + cx.theme().colors().text } else { - theme.syntax.get("comment").color.unwrap_or_default() + cx.theme().syntax_color("comment") }; h_stack() @@ -216,14 +214,13 @@ impl Buffer { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); let rows = self.render_rows(cx); v_stack() .flex_1() .w_full() .h_full() - .bg(theme.editor) + .bg(cx.theme().colors().editor) .children(rows) } } @@ -246,8 +243,6 @@ mod stories { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - let theme = theme(cx); - Story::container(cx) .child(Story::title_for::<_, Buffer>(cx)) .child(Story::label(cx, "Default")) @@ -257,14 +252,14 @@ mod stories { div() .w(rems(64.)) .h_96() - .child(hello_world_rust_buffer_example(&theme)), + .child(hello_world_rust_buffer_example(cx)), ) .child(Story::label(cx, "Hello World (Rust) with Status")) .child( div() .w(rems(64.)) .h_96() - .child(hello_world_rust_buffer_with_status_example(&theme)), + .child(hello_world_rust_buffer_with_status_example(cx)), ) } } diff --git a/crates/ui2/src/components/buffer_search.rs b/crates/ui2/src/components/buffer_search.rs index c5539f0a4a..5d7de1b408 100644 --- a/crates/ui2/src/components/buffer_search.rs +++ b/crates/ui2/src/components/buffer_search.rs @@ -30,9 +30,7 @@ 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( + h_stack().bg(cx.theme().colors().toolbar).p_2().child( h_stack().child(Input::new("Search")).child( IconButton::::new("replace", Icon::Replace) .when(self.is_replace_open, |this| this.color(IconColor::Accent)) diff --git a/crates/ui2/src/components/collab_panel.rs b/crates/ui2/src/components/collab_panel.rs index a8552c0f23..a0e3b55f63 100644 --- a/crates/ui2/src/components/collab_panel.rs +++ b/crates/ui2/src/components/collab_panel.rs @@ -15,27 +15,29 @@ impl CollabPanel { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - v_stack() .id(self.id.clone()) .h_full() - .bg(theme.surface) + .bg(cx.theme().colors().surface) .child( v_stack() .id("crdb") .w_full() .overflow_y_scroll() .child( - div().pb_1().border_color(theme.border).border_b().child( - List::new(static_collab_panel_current_call()) - .header( - ListHeader::new("CRDB") - .left_icon(Icon::Hash.into()) - .toggle(ToggleState::Toggled), - ) - .toggle(ToggleState::Toggled), - ), + div() + .pb_1() + .border_color(cx.theme().colors().border) + .border_b() + .child( + List::new(static_collab_panel_current_call()) + .header( + ListHeader::new("CRDB") + .left_icon(Icon::Hash.into()) + .toggle(ToggleState::Toggled), + ) + .toggle(ToggleState::Toggled), + ), ) .child( v_stack().id("channels").py_1().child( @@ -71,13 +73,13 @@ impl CollabPanel { .h_7() .px_2() .border_t() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .flex() .items_center() .child( div() .text_sm() - .text_color(theme.text_placeholder) + .text_color(cx.theme().colors().text_placeholder) .child("Find..."), ), ) diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 812221036a..8345be1b35 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -44,13 +44,11 @@ impl ContextMenu { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - v_stack() .flex() - .bg(theme.elevated_surface) + .bg(cx.theme().colors().elevated_surface) .border() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .child( List::new( self.items diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index 980a1c98aa..06e242b1ef 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -66,8 +66,6 @@ impl IconButton { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let icon_color = match (self.state, self.color) { (InteractionState::Disabled, _) => IconColor::Disabled, _ => self.color, @@ -75,14 +73,14 @@ impl IconButton { let (bg_color, bg_hover_color, bg_active_color) = match self.variant { ButtonVariant::Filled => ( - theme.filled_element, - theme.filled_element_hover, - theme.filled_element_active, + cx.theme().colors().element, + cx.theme().colors().element_hover, + cx.theme().colors().element_active, ), ButtonVariant::Ghost => ( - theme.ghost_element, - theme.ghost_element_hover, - theme.ghost_element_active, + cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, ), }; diff --git a/crates/ui2/src/components/keybinding.rs b/crates/ui2/src/components/keybinding.rs index 455cfe5b59..88cabbdc88 100644 --- a/crates/ui2/src/components/keybinding.rs +++ b/crates/ui2/src/components/keybinding.rs @@ -60,15 +60,13 @@ impl Key { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - div() .px_2() .py_0() .rounded_md() .text_sm() - .text_color(theme.text) - .bg(theme.filled_element) + .text_color(cx.theme().colors().text) + .bg(cx.theme().colors().element) .child(self.key.clone()) } } diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index 9557e68d7f..1668592a38 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -89,8 +89,6 @@ impl ListHeader { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let is_toggleable = self.toggleable != Toggleable::NotToggleable; let is_toggled = self.toggleable.is_toggled(); @@ -99,9 +97,10 @@ impl ListHeader { h_stack() .flex_1() .w_full() - .bg(theme.surface) + .bg(cx.theme().colors().surface) .when(self.state == InteractionState::Focused, |this| { - this.border().border_color(theme.border_focused) + this.border() + .border_color(cx.theme().colors().border_focused) }) .relative() .child( @@ -363,7 +362,6 @@ impl ListEntry { fn render(mut self, _view: &mut V, cx: &mut ViewContext) -> impl Component { let settings = user_settings(cx); - let theme = theme(cx); let left_content = match self.left_content.clone() { Some(LeftContent::Icon(i)) => Some( @@ -385,9 +383,10 @@ impl ListEntry { div() .relative() .group("") - .bg(theme.surface) + .bg(cx.theme().colors().surface) .when(self.state == InteractionState::Focused, |this| { - this.border().border_color(theme.border_focused) + this.border() + .border_color(cx.theme().colors().border_focused) }) .child( sized_item @@ -399,11 +398,11 @@ impl ListEntry { .h_full() .flex() .justify_center() - .group_hover("", |style| style.bg(theme.border_focused)) + .group_hover("", |style| style.bg(cx.theme().colors().border_focused)) .child( h_stack() .child(div().w_px().h_full()) - .child(div().w_px().h_full().bg(theme.border)), + .child(div().w_px().h_full().bg(cx.theme().colors().border)), ) })) .flex() @@ -472,19 +471,18 @@ impl ListDetailsEntry { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); let settings = user_settings(cx); let (item_bg, item_bg_hover, item_bg_active) = match self.seen { true => ( - theme.ghost_element, - theme.ghost_element_hover, - theme.ghost_element_active, + cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, ), false => ( - theme.filled_element, - theme.filled_element_hover, - theme.filled_element_active, + cx.theme().colors().element, + cx.theme().colors().element_hover, + cx.theme().colors().element_active, ), }; @@ -524,9 +522,7 @@ impl ListSeparator { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - - div().h_px().w_full().bg(theme.border) + div().h_px().w_full().bg(cx.theme().colors().border) } } diff --git a/crates/ui2/src/components/modal.rs b/crates/ui2/src/components/modal.rs index 7c3efe79ba..26986474e0 100644 --- a/crates/ui2/src/components/modal.rs +++ b/crates/ui2/src/components/modal.rs @@ -39,22 +39,20 @@ impl Modal { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - v_stack() .id(self.id.clone()) .w_96() // .rounded_xl() - .bg(theme.background) + .bg(cx.theme().colors().background) .border() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .shadow_2xl() .child( h_stack() .justify_between() .p_1() .border_b() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .child(div().children(self.title.clone().map(|t| Label::new(t)))) .child(IconButton::new("close", Icon::Close)), ) @@ -65,7 +63,7 @@ impl Modal { this.child( h_stack() .border_t() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .p_1() .justify_end() .children(self.secondary_action) diff --git a/crates/ui2/src/components/multi_buffer.rs b/crates/ui2/src/components/multi_buffer.rs index 696fc77a62..ea130f20bd 100644 --- a/crates/ui2/src/components/multi_buffer.rs +++ b/crates/ui2/src/components/multi_buffer.rs @@ -12,8 +12,6 @@ impl MultiBuffer { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - v_stack() .w_full() .h_full() @@ -26,7 +24,7 @@ impl MultiBuffer { .items_center() .justify_between() .p_4() - .bg(theme.editor_subheader) + .bg(cx.theme().colors().editor_subheader) .child(Label::new("main.rs")) .child(IconButton::new("arrow_up_right", Icon::ArrowUpRight)), ) @@ -50,17 +48,15 @@ mod stories { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - let theme = theme(cx); - Story::container(cx) .child(Story::title_for::<_, MultiBuffer>(cx)) .child(Story::label(cx, "Default")) .child(MultiBuffer::new(vec![ - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), + hello_world_rust_buffer_example(cx), + hello_world_rust_buffer_example(cx), + hello_world_rust_buffer_example(cx), + hello_world_rust_buffer_example(cx), + hello_world_rust_buffer_example(cx), ])) } } diff --git a/crates/ui2/src/components/notification_toast.rs b/crates/ui2/src/components/notification_toast.rs index f7d280ed16..59078c98f4 100644 --- a/crates/ui2/src/components/notification_toast.rs +++ b/crates/ui2/src/components/notification_toast.rs @@ -1,6 +1,7 @@ use gpui2::rems; -use crate::{h_stack, prelude::*, Icon}; +use crate::prelude::*; +use crate::{h_stack, Icon}; #[derive(Component)] pub struct NotificationToast { @@ -22,8 +23,6 @@ impl NotificationToast { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - h_stack() .z_index(5) .absolute() @@ -35,7 +34,7 @@ impl NotificationToast { .px_1p5() .rounded_lg() .shadow_md() - .bg(theme.elevated_surface) + .bg(cx.theme().colors().elevated_surface) .child(div().size_full().child(self.label.clone())) } } diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index 6872f116e9..10b0e07af6 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -12,15 +12,13 @@ impl NotificationsPanel { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - div() .id(self.id.clone()) .flex() .flex_col() .w_full() .h_full() - .bg(theme.surface) + .bg(cx.theme().colors().surface) .child( div() .id("header") diff --git a/crates/ui2/src/components/palette.rs b/crates/ui2/src/components/palette.rs index e47f6a4cea..a1f3eb7e1c 100644 --- a/crates/ui2/src/components/palette.rs +++ b/crates/ui2/src/components/palette.rs @@ -43,22 +43,20 @@ impl Palette { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - v_stack() .id(self.id.clone()) .w_96() .rounded_lg() - .bg(theme.elevated_surface) + .bg(cx.theme().colors().elevated_surface) .border() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .child( v_stack() .gap_px() .child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child( Label::new(self.input_placeholder.clone()).color(LabelColor::Placeholder), ))) - .child(div().h_px().w_full().bg(theme.filled_element)) + .child(div().h_px().w_full().bg(cx.theme().colors().element)) .child( v_stack() .id("items") @@ -88,8 +86,12 @@ impl Palette { .px_2() .py_0p5() .rounded_lg() - .hover(|style| style.bg(theme.ghost_element_hover)) - .active(|style| style.bg(theme.ghost_element_active)) + .hover(|style| { + style.bg(cx.theme().colors().ghost_element_hover) + }) + .active(|style| { + style.bg(cx.theme().colors().ghost_element_active) + }) .child(item) })), ), diff --git a/crates/ui2/src/components/panel.rs b/crates/ui2/src/components/panel.rs index 12d2207ffd..c4a9ac5111 100644 --- a/crates/ui2/src/components/panel.rs +++ b/crates/ui2/src/components/panel.rs @@ -93,8 +93,6 @@ impl Panel { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let current_size = self.width.unwrap_or(self.initial_width); v_stack() @@ -111,8 +109,8 @@ impl Panel { .when(self.current_side == PanelSide::Bottom, |this| { this.border_b().w_full().h(current_size) }) - .bg(theme.surface) - .border_color(theme.border) + .bg(cx.theme().colors().surface) + .border_color(cx.theme().colors().border) .children(self.children) } } diff --git a/crates/ui2/src/components/panes.rs b/crates/ui2/src/components/panes.rs index 854786ebaa..167e837083 100644 --- a/crates/ui2/src/components/panes.rs +++ b/crates/ui2/src/components/panes.rs @@ -90,8 +90,6 @@ impl PaneGroup { } fn render(self, view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - if !self.panes.is_empty() { let el = div() .flex() @@ -115,7 +113,7 @@ impl PaneGroup { .gap_px() .w_full() .h_full() - .bg(theme.editor) + .bg(cx.theme().colors().editor) .children(self.groups.into_iter().map(|group| group.render(view, cx))); if self.split_direction == SplitDirection::Horizontal { diff --git a/crates/ui2/src/components/player_stack.rs b/crates/ui2/src/components/player_stack.rs index ced761a086..1a1231e6c4 100644 --- a/crates/ui2/src/components/player_stack.rs +++ b/crates/ui2/src/components/player_stack.rs @@ -14,9 +14,7 @@ impl PlayerStack { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); let player = self.player_with_call_status.get_player(); - self.player_with_call_status.get_call_status(); let followers = self .player_with_call_status @@ -50,7 +48,7 @@ impl PlayerStack { .pl_1() .rounded_lg() .bg(if followers.is_none() { - theme.transparent + cx.theme().styles.system.transparent } else { player.selection_color(cx) }) diff --git a/crates/ui2/src/components/project_panel.rs b/crates/ui2/src/components/project_panel.rs index 84c68119fe..76fa50d338 100644 --- a/crates/ui2/src/components/project_panel.rs +++ b/crates/ui2/src/components/project_panel.rs @@ -14,15 +14,13 @@ impl ProjectPanel { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - div() .id(self.id.clone()) .flex() .flex_col() .w_full() .h_full() - .bg(theme.surface) + .bg(cx.theme().colors().surface) .child( div() .id("project-panel-contents") diff --git a/crates/ui2/src/components/status_bar.rs b/crates/ui2/src/components/status_bar.rs index a23040193f..136472f605 100644 --- a/crates/ui2/src/components/status_bar.rs +++ b/crates/ui2/src/components/status_bar.rs @@ -86,8 +86,6 @@ impl StatusBar { view: &mut Workspace, cx: &mut ViewContext, ) -> impl Component { - let theme = theme(cx); - div() .py_0p5() .px_1() @@ -95,7 +93,7 @@ impl StatusBar { .items_center() .justify_between() .w_full() - .bg(theme.status_bar) + .bg(cx.theme().colors().status_bar) .child(self.left_tools(view, cx)) .child(self.right_tools(view, cx)) } diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index d784ec0174..5f20af0955 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -87,7 +87,6 @@ impl Tab { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict; let is_deleted = self.fs_status == FileSystemStatus::Deleted; @@ -110,14 +109,14 @@ impl Tab { let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current { true => ( - theme.ghost_element, - theme.ghost_element_hover, - theme.ghost_element_active, + cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, ), false => ( - theme.filled_element, - theme.filled_element_hover, - theme.filled_element_active, + cx.theme().colors().element, + cx.theme().colors().element_hover, + cx.theme().colors().element_active, ), }; diff --git a/crates/ui2/src/components/tab_bar.rs b/crates/ui2/src/components/tab_bar.rs index da0a41a1bf..550105b98e 100644 --- a/crates/ui2/src/components/tab_bar.rs +++ b/crates/ui2/src/components/tab_bar.rs @@ -24,15 +24,13 @@ impl TabBar { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let (can_navigate_back, can_navigate_forward) = self.can_navigate; div() .id(self.id.clone()) .w_full() .flex() - .bg(theme.tab_bar) + .bg(cx.theme().colors().tab_bar) // Left Side .child( div() diff --git a/crates/ui2/src/components/terminal.rs b/crates/ui2/src/components/terminal.rs index a751d47dfc..051ebf7315 100644 --- a/crates/ui2/src/components/terminal.rs +++ b/crates/ui2/src/components/terminal.rs @@ -12,8 +12,6 @@ impl Terminal { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let can_navigate_back = true; let can_navigate_forward = false; @@ -26,7 +24,7 @@ impl Terminal { div() .w_full() .flex() - .bg(theme.surface) + .bg(cx.theme().colors().surface) .child( div().px_1().flex().flex_none().gap_2().child( div() @@ -73,7 +71,7 @@ impl Terminal { height: rems(36.).into(), }, ) - .child(crate::static_data::terminal_buffer(&theme)), + .child(crate::static_data::terminal_buffer(cx)), ) } } diff --git a/crates/ui2/src/components/title_bar.rs b/crates/ui2/src/components/title_bar.rs index 4b3b125dea..2fa201440a 100644 --- a/crates/ui2/src/components/title_bar.rs +++ b/crates/ui2/src/components/title_bar.rs @@ -89,7 +89,6 @@ impl Render for TitleBar { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Div { - let theme = theme(cx); let settings = user_settings(cx); // let has_focus = cx.window_is_active(); @@ -106,7 +105,7 @@ impl Render for TitleBar { .items_center() .justify_between() .w_full() - .bg(theme.background) + .bg(cx.theme().colors().background) .py_1() .child( div() diff --git a/crates/ui2/src/components/toast.rs b/crates/ui2/src/components/toast.rs index 814e91c498..3b81ac42b4 100644 --- a/crates/ui2/src/components/toast.rs +++ b/crates/ui2/src/components/toast.rs @@ -37,8 +37,6 @@ impl Toast { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let mut div = div(); if self.origin == ToastOrigin::Bottom { @@ -56,7 +54,7 @@ impl Toast { .rounded_lg() .shadow_md() .overflow_hidden() - .bg(theme.elevated_surface) + .bg(cx.theme().colors().elevated_surface) .children(self.children) } } diff --git a/crates/ui2/src/components/toolbar.rs b/crates/ui2/src/components/toolbar.rs index 4b35e2d9d2..05a5c991d6 100644 --- a/crates/ui2/src/components/toolbar.rs +++ b/crates/ui2/src/components/toolbar.rs @@ -55,10 +55,8 @@ impl Toolbar { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - div() - .bg(theme.toolbar) + .bg(cx.theme().colors().toolbar) .p_2() .flex() .justify_between() @@ -87,8 +85,6 @@ mod stories { 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::label(cx, "Default")) @@ -100,21 +96,21 @@ mod stories { Symbol(vec![ HighlightedText { text: "impl ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "ToolbarStory".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, ]), Symbol(vec![ HighlightedText { text: "fn ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "render".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, ]), ], diff --git a/crates/ui2/src/components/traffic_lights.rs b/crates/ui2/src/components/traffic_lights.rs index 8ee19d26f5..9080276cdd 100644 --- a/crates/ui2/src/components/traffic_lights.rs +++ b/crates/ui2/src/components/traffic_lights.rs @@ -22,13 +22,13 @@ impl TrafficLight { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); + let system_colors = &cx.theme().styles.system; let fill = match (self.window_has_focus, self.color) { - (true, TrafficLightColor::Red) => theme.mac_os_traffic_light_red, - (true, TrafficLightColor::Yellow) => theme.mac_os_traffic_light_yellow, - (true, TrafficLightColor::Green) => theme.mac_os_traffic_light_green, - (false, _) => theme.filled_element, + (true, TrafficLightColor::Red) => system_colors.mac_os_traffic_light_red, + (true, TrafficLightColor::Yellow) => system_colors.mac_os_traffic_light_yellow, + (true, TrafficLightColor::Green) => system_colors.mac_os_traffic_light_green, + (false, _) => cx.theme().colors().element, }; div().w_3().h_3().rounded_full().bg(fill) diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index 78ab6232a8..0e31c6b9ad 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -179,8 +179,6 @@ 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 // we don't have `cx.global::()` in event handlers at the moment. // Need to talk with Nathan/Antonio about this. @@ -216,8 +214,8 @@ impl Render for Workspace { .gap_0() .justify_start() .items_start() - .text_color(theme.text) - .bg(theme.background) + .text_color(cx.theme().colors().text) + .bg(cx.theme().colors().background) .child(self.title_bar.clone()) .child( div() @@ -228,7 +226,7 @@ impl Render for Workspace { .overflow_hidden() .border_t() .border_b() - .border_color(theme.border) + .border_color(cx.theme().colors().border) .children( Some( Panel::new("project-panel-outer", cx) diff --git a/crates/ui2/src/elements/avatar.rs b/crates/ui2/src/elements/avatar.rs index f008eeb479..ff92021b11 100644 --- a/crates/ui2/src/elements/avatar.rs +++ b/crates/ui2/src/elements/avatar.rs @@ -22,8 +22,6 @@ impl Avatar { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let mut img = img(); if self.shape == Shape::Circle { @@ -34,7 +32,8 @@ impl Avatar { img.uri(self.src.clone()) .size_4() - .bg(theme.image_fallback_background) + // todo!(Pull the avatar fallback background from the theme.) + .bg(gpui2::red()) } } diff --git a/crates/ui2/src/elements/button.rs b/crates/ui2/src/elements/button.rs index d27a0537d8..e63269197c 100644 --- a/crates/ui2/src/elements/button.rs +++ b/crates/ui2/src/elements/button.rs @@ -21,29 +21,23 @@ pub enum ButtonVariant { impl ButtonVariant { pub fn bg_color(&self, cx: &mut WindowContext) -> Hsla { - let theme = theme(cx); - match self { - ButtonVariant::Ghost => theme.ghost_element, - ButtonVariant::Filled => theme.filled_element, + ButtonVariant::Ghost => cx.theme().colors().ghost_element, + ButtonVariant::Filled => cx.theme().colors().element, } } pub fn bg_color_hover(&self, cx: &mut WindowContext) -> Hsla { - let theme = theme(cx); - match self { - ButtonVariant::Ghost => theme.ghost_element_hover, - ButtonVariant::Filled => theme.filled_element_hover, + ButtonVariant::Ghost => cx.theme().colors().ghost_element_hover, + ButtonVariant::Filled => cx.theme().colors().element_hover, } } pub fn bg_color_active(&self, cx: &mut WindowContext) -> Hsla { - let theme = theme(cx); - match self { - ButtonVariant::Ghost => theme.ghost_element_active, - ButtonVariant::Filled => theme.filled_element_active, + ButtonVariant::Ghost => cx.theme().colors().ghost_element_active, + ButtonVariant::Filled => cx.theme().colors().element_active, } } } diff --git a/crates/ui2/src/elements/details.rs b/crates/ui2/src/elements/details.rs index eca7798c82..1d22c81774 100644 --- a/crates/ui2/src/elements/details.rs +++ b/crates/ui2/src/elements/details.rs @@ -1,4 +1,5 @@ -use crate::{prelude::*, v_stack, ButtonGroup}; +use crate::prelude::*; +use crate::{v_stack, ButtonGroup}; #[derive(Component)] pub struct Details { @@ -27,13 +28,11 @@ impl Details { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - v_stack() .p_1() .gap_0p5() .text_xs() - .text_color(theme.text) + .text_color(cx.theme().colors().text) .size_full() .child(self.text) .children(self.meta.map(|m| m)) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index 4e4ec2bce7..6c1b3a4f08 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -26,13 +26,14 @@ pub enum IconColor { impl IconColor { pub fn color(self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); + let theme_colors = cx.theme().colors(); + match self { - IconColor::Default => gpui2::red(), - IconColor::Muted => gpui2::red(), - IconColor::Disabled => gpui2::red(), - IconColor::Placeholder => gpui2::red(), - IconColor::Accent => gpui2::red(), + IconColor::Default => theme_colors.icon, + IconColor::Muted => theme_colors.icon_muted, + IconColor::Disabled => theme_colors.icon_disabled, + IconColor::Placeholder => theme_colors.icon_placeholder, + IconColor::Accent => theme_colors.icon_accent, IconColor::Error => gpui2::red(), IconColor::Warning => gpui2::red(), IconColor::Success => gpui2::red(), diff --git a/crates/ui2/src/elements/input.rs b/crates/ui2/src/elements/input.rs index e9e92dd0a6..3f82512b84 100644 --- a/crates/ui2/src/elements/input.rs +++ b/crates/ui2/src/elements/input.rs @@ -57,18 +57,16 @@ impl Input { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - let (input_bg, input_hover_bg, input_active_bg) = match self.variant { InputVariant::Ghost => ( - theme.ghost_element, - theme.ghost_element_hover, - theme.ghost_element_active, + cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, ), InputVariant::Filled => ( - theme.filled_element, - theme.filled_element_hover, - theme.filled_element_active, + cx.theme().colors().element, + cx.theme().colors().element_hover, + cx.theme().colors().element_active, ), }; @@ -90,7 +88,7 @@ impl Input { .w_full() .px_2() .border() - .border_color(theme.transparent) + .border_color(cx.theme().styles.system.transparent) .bg(input_bg) .hover(|style| style.bg(input_hover_bg)) .active(|style| style.bg(input_active_bg)) diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index 4d336345fb..ee8ac9a636 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -18,18 +18,16 @@ pub enum LabelColor { impl LabelColor { pub fn hsla(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - match self { - Self::Default => theme.text, - Self::Muted => theme.text_muted, + Self::Default => cx.theme().colors().text, + Self::Muted => cx.theme().colors().text_muted, Self::Created => gpui2::red(), Self::Modified => gpui2::red(), Self::Deleted => gpui2::red(), - Self::Disabled => theme.text_disabled, + Self::Disabled => cx.theme().colors().text_disabled, Self::Hidden => gpui2::red(), - Self::Placeholder => theme.text_placeholder, - Self::Accent => gpui2::red(), + Self::Placeholder => cx.theme().colors().text_placeholder, + Self::Accent => cx.theme().colors().text_accent, } } } @@ -126,9 +124,7 @@ impl HighlightedLabel { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - - let highlight_color = theme.text_accent; + let highlight_color = cx.theme().colors().text_accent; let mut highlight_indices = self.highlight_indices.iter().copied().peekable(); diff --git a/crates/ui2/src/elements/player.rs b/crates/ui2/src/elements/player.rs index 5bf890b8bb..8e3ad5c3a8 100644 --- a/crates/ui2/src/elements/player.rs +++ b/crates/ui2/src/elements/player.rs @@ -1,6 +1,6 @@ use gpui2::{Hsla, ViewContext}; -use crate::theme; +use crate::prelude::*; #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub enum PlayerStatus { @@ -139,13 +139,11 @@ impl Player { } pub fn cursor_color(&self, cx: &mut ViewContext) -> Hsla { - let theme = theme(cx); - theme.players[self.index].cursor + cx.theme().styles.player.0[self.index].cursor } pub fn selection_color(&self, cx: &mut ViewContext) -> Hsla { - let theme = theme(cx); - theme.players[self.index].selection + cx.theme().styles.player.0[self.index].selection } pub fn avatar_src(&self) -> &str { diff --git a/crates/ui2/src/elements/tool_divider.rs b/crates/ui2/src/elements/tool_divider.rs index e1ebb294a0..8a9bbad97f 100644 --- a/crates/ui2/src/elements/tool_divider.rs +++ b/crates/ui2/src/elements/tool_divider.rs @@ -9,8 +9,6 @@ impl ToolDivider { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let theme = theme(cx); - - div().w_px().h_3().bg(theme.border) + div().w_px().h_3().bg(cx.theme().colors().border) } } diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 63405fc2cb..b424ce6123 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -6,7 +6,7 @@ pub use gpui2::{ pub use crate::elevation::*; use crate::settings::user_settings; pub use crate::ButtonVariant; -pub use theme2::theme; +pub use theme2::ActiveTheme; use gpui2::{rems, Hsla, Rems}; use strum::EnumIter; @@ -54,15 +54,13 @@ pub enum GitStatus { impl GitStatus { pub fn hsla(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - match self { - Self::None => theme.transparent, - Self::Created => theme.git_created, - Self::Modified => theme.git_modified, - Self::Deleted => theme.git_deleted, - Self::Conflict => theme.git_conflict, - Self::Renamed => theme.git_renamed, + Self::None => cx.theme().styles.system.transparent, + Self::Created => cx.theme().styles.git.created, + Self::Modified => cx.theme().styles.git.modified, + Self::Deleted => cx.theme().styles.git.deleted, + Self::Conflict => cx.theme().styles.git.conflict, + Self::Renamed => cx.theme().styles.git.renamed, } } } diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 68f1e36b2c..7062c81954 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -1,12 +1,12 @@ use std::path::PathBuf; use std::str::FromStr; -use gpui2::ViewContext; +use gpui2::{AppContext, ViewContext}; use rand::Rng; -use theme2::Theme; +use theme2::ActiveTheme; use crate::{ - theme, Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, + Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem, Livestream, MicStatus, ModifierKeys, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus, @@ -643,8 +643,6 @@ pub fn empty_buffer_example() -> Buffer { } pub fn hello_world_rust_editor_example(cx: &mut ViewContext) -> EditorPane { - let theme = theme(cx); - EditorPane::new( cx, static_tabs_example(), @@ -652,29 +650,29 @@ pub fn hello_world_rust_editor_example(cx: &mut ViewContext) -> Edit vec![Symbol(vec![ HighlightedText { text: "fn ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "main".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, ])], - hello_world_rust_buffer_example(&theme), + hello_world_rust_buffer_example(cx), ) } -pub fn hello_world_rust_buffer_example(theme: &Theme) -> Buffer { +pub fn hello_world_rust_buffer_example(cx: &AppContext) -> Buffer { Buffer::new("hello-world-rust-buffer") .set_title("hello_world.rs".to_string()) .set_path("src/hello_world.rs".to_string()) .set_language("rust".to_string()) .set_rows(Some(BufferRows { show_line_numbers: true, - rows: hello_world_rust_buffer_rows(theme), + rows: hello_world_rust_buffer_rows(cx), })) } -pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { +pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec { let show_line_number = true; vec![ @@ -686,15 +684,15 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { highlighted_texts: vec![ HighlightedText { text: "fn ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "main".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, HighlightedText { text: "() {".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, ], }), @@ -710,7 +708,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { highlighted_texts: vec![HighlightedText { text: " // Statements here are executed when the compiled binary is called." .to_string(), - color: theme.syntax.color("comment"), + color: cx.theme().syntax_color("comment"), }], }), cursors: None, @@ -733,7 +731,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: " // Print text to the console.".to_string(), - color: theme.syntax.color("comment"), + color: cx.theme().syntax_color("comment"), }], }), cursors: None, @@ -748,15 +746,15 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { highlighted_texts: vec![ HighlightedText { text: " println!(".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, HighlightedText { text: "\"Hello, world!\"".to_string(), - color: theme.syntax.color("string"), + color: cx.theme().syntax_color("string"), }, HighlightedText { text: ");".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, ], }), @@ -771,7 +769,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: "}".to_string(), - color: theme.text, + color: cx.theme().colors().text, }], }), cursors: None, @@ -782,8 +780,6 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { } pub fn hello_world_rust_editor_with_status_example(cx: &mut ViewContext) -> EditorPane { - let theme = theme(cx); - EditorPane::new( cx, static_tabs_example(), @@ -791,29 +787,29 @@ pub fn hello_world_rust_editor_with_status_example(cx: &mut ViewContext Buffer { +pub fn hello_world_rust_buffer_with_status_example(cx: &AppContext) -> Buffer { Buffer::new("hello-world-rust-buffer-with-status") .set_title("hello_world.rs".to_string()) .set_path("src/hello_world.rs".to_string()) .set_language("rust".to_string()) .set_rows(Some(BufferRows { show_line_numbers: true, - rows: hello_world_rust_with_status_buffer_rows(theme), + rows: hello_world_rust_with_status_buffer_rows(cx), })) } -pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec { +pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec { let show_line_number = true; vec![ @@ -825,15 +821,15 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec highlighted_texts: vec![ HighlightedText { text: "fn ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "main".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, HighlightedText { text: "() {".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, ], }), @@ -849,7 +845,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec highlighted_texts: vec![HighlightedText { text: "// Statements here are executed when the compiled binary is called." .to_string(), - color: theme.syntax.color("comment"), + color: cx.theme().syntax_color("comment"), }], }), cursors: None, @@ -872,7 +868,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: " // Print text to the console.".to_string(), - color: theme.syntax.color("comment"), + color: cx.theme().syntax_color("comment"), }], }), cursors: None, @@ -887,15 +883,15 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec highlighted_texts: vec![ HighlightedText { text: " println!(".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, HighlightedText { text: "\"Hello, world!\"".to_string(), - color: theme.syntax.color("string"), + color: cx.theme().syntax_color("string"), }, HighlightedText { text: ");".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, ], }), @@ -910,7 +906,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: "}".to_string(), - color: theme.text, + color: cx.theme().colors().text, }], }), cursors: None, @@ -924,7 +920,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: "".to_string(), - color: theme.text, + color: cx.theme().colors().text, }], }), cursors: None, @@ -938,7 +934,7 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: "// Marshall and Nate were here".to_string(), - color: theme.syntax.color("comment"), + color: cx.theme().syntax_color("comment"), }], }), cursors: None, @@ -948,16 +944,16 @@ pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec ] } -pub fn terminal_buffer(theme: &Theme) -> Buffer { +pub fn terminal_buffer(cx: &AppContext) -> Buffer { Buffer::new("terminal") .set_title("zed — fish".to_string()) .set_rows(Some(BufferRows { show_line_numbers: false, - rows: terminal_buffer_rows(theme), + rows: terminal_buffer_rows(cx), })) } -pub fn terminal_buffer_rows(theme: &Theme) -> Vec { +pub fn terminal_buffer_rows(cx: &AppContext) -> Vec { let show_line_number = false; vec![ @@ -969,31 +965,31 @@ pub fn terminal_buffer_rows(theme: &Theme) -> Vec { highlighted_texts: vec![ HighlightedText { text: "maxdeviant ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, HighlightedText { text: "in ".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, HighlightedText { text: "profaned-capital ".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, HighlightedText { text: "in ".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, HighlightedText { text: "~/p/zed ".to_string(), - color: theme.syntax.color("function"), + color: cx.theme().syntax_color("function"), }, HighlightedText { text: "on ".to_string(), - color: theme.text, + color: cx.theme().colors().text, }, HighlightedText { text: " gpui2-ui ".to_string(), - color: theme.syntax.color("keyword"), + color: cx.theme().syntax_color("keyword"), }, ], }), @@ -1008,7 +1004,7 @@ pub fn terminal_buffer_rows(theme: &Theme) -> Vec { line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: "λ ".to_string(), - color: theme.syntax.color("string"), + color: cx.theme().syntax_color("string"), }], }), cursors: None, diff --git a/crates/ui2/src/story.rs b/crates/ui2/src/story.rs index d2813bd174..dea4e342b4 100644 --- a/crates/ui2/src/story.rs +++ b/crates/ui2/src/story.rs @@ -6,8 +6,6 @@ pub struct Story {} impl Story { pub fn container(cx: &mut ViewContext) -> Div { - let theme = theme(cx); - div() .size_full() .flex() @@ -15,15 +13,13 @@ impl Story { .pt_2() .px_4() .font("Zed Mono") - .bg(theme.background) + .bg(cx.theme().colors().background) } pub fn title(cx: &mut ViewContext, title: &str) -> impl Component { - let theme = theme(cx); - div() .text_xl() - .text_color(theme.text) + .text_color(cx.theme().colors().text) .child(title.to_owned()) } @@ -32,13 +28,11 @@ impl Story { } pub fn label(cx: &mut ViewContext, label: &str) -> impl Component { - let theme = theme(cx); - div() .mt_4() .mb_2() .text_xs() - .text_color(theme.text) + .text_color(cx.theme().colors().text) .child(label.to_owned()) } } From 36a73d657a6fe1d53e5d0f81f60bfa83d00b77ff Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 04:05:50 +0100 Subject: [PATCH 11/18] Remove old `Theme` definition (#3195) This PR removes the old `Theme` definition in favor of the new `ThemeVariant`s. The new `SyntaxStyles` have been reverted to the old `SyntaxTheme` that operates by storing the syntax styles as a vector of `gpui2::HighlightStyle`s. This is necessary for the intended usage by `language2`, where we find the longest key in the theme's syntax styles that matches the capture name: https://github.com/zed-industries/zed/blob/18431051d9d750d9e66284a71f7a55a1e31c1374/crates/language2/src/highlight_map.rs#L15-L41 --- Cargo.lock | 15 - Cargo.toml | 1 - crates/language2/src/language2.rs | 10 +- crates/storybook2/src/storybook2.rs | 4 +- crates/theme2/src/colors.rs | 4 +- crates/theme2/src/default_colors.rs | 642 +++--------------- crates/theme2/src/default_theme.rs | 16 +- crates/theme2/src/registry.rs | 63 +- crates/theme2/src/settings.rs | 10 +- crates/theme2/src/syntax.rs | 238 +------ crates/theme2/src/theme2.rs | 137 +--- crates/theme2/src/themes/andromeda.rs | 130 ---- crates/theme2/src/themes/atelier_cave_dark.rs | 136 ---- .../theme2/src/themes/atelier_cave_light.rs | 136 ---- crates/theme2/src/themes/atelier_dune_dark.rs | 136 ---- .../theme2/src/themes/atelier_dune_light.rs | 136 ---- .../theme2/src/themes/atelier_estuary_dark.rs | 136 ---- .../src/themes/atelier_estuary_light.rs | 136 ---- .../theme2/src/themes/atelier_forest_dark.rs | 136 ---- .../theme2/src/themes/atelier_forest_light.rs | 136 ---- .../theme2/src/themes/atelier_heath_dark.rs | 136 ---- .../theme2/src/themes/atelier_heath_light.rs | 136 ---- .../src/themes/atelier_lakeside_dark.rs | 136 ---- .../src/themes/atelier_lakeside_light.rs | 136 ---- .../theme2/src/themes/atelier_plateau_dark.rs | 136 ---- .../src/themes/atelier_plateau_light.rs | 136 ---- .../theme2/src/themes/atelier_savanna_dark.rs | 136 ---- .../src/themes/atelier_savanna_light.rs | 136 ---- .../theme2/src/themes/atelier_seaside_dark.rs | 136 ---- .../src/themes/atelier_seaside_light.rs | 136 ---- .../src/themes/atelier_sulphurpool_dark.rs | 136 ---- .../src/themes/atelier_sulphurpool_light.rs | 136 ---- crates/theme2/src/themes/ayu_dark.rs | 130 ---- crates/theme2/src/themes/ayu_light.rs | 130 ---- crates/theme2/src/themes/ayu_mirage.rs | 130 ---- crates/theme2/src/themes/gruvbox_dark.rs | 131 ---- crates/theme2/src/themes/gruvbox_dark_hard.rs | 131 ---- crates/theme2/src/themes/gruvbox_dark_soft.rs | 131 ---- crates/theme2/src/themes/gruvbox_light.rs | 131 ---- .../theme2/src/themes/gruvbox_light_hard.rs | 131 ---- .../theme2/src/themes/gruvbox_light_soft.rs | 131 ---- crates/theme2/src/themes/mod.rs | 79 --- crates/theme2/src/themes/one_dark.rs | 131 ---- crates/theme2/src/themes/one_light.rs | 131 ---- crates/theme2/src/themes/rose_pine.rs | 132 ---- crates/theme2/src/themes/rose_pine_dawn.rs | 132 ---- crates/theme2/src/themes/rose_pine_moon.rs | 132 ---- crates/theme2/src/themes/sandcastle.rs | 130 ---- crates/theme2/src/themes/solarized_dark.rs | 130 ---- crates/theme2/src/themes/solarized_light.rs | 130 ---- crates/theme2/src/themes/summercamp.rs | 130 ---- crates/theme_converter/Cargo.toml | 18 - crates/theme_converter/src/main.rs | 390 ----------- crates/theme_converter/src/theme_printer.rs | 174 ----- 54 files changed, 173 insertions(+), 6832 deletions(-) delete mode 100644 crates/theme2/src/themes/andromeda.rs delete mode 100644 crates/theme2/src/themes/atelier_cave_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_cave_light.rs delete mode 100644 crates/theme2/src/themes/atelier_dune_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_dune_light.rs delete mode 100644 crates/theme2/src/themes/atelier_estuary_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_estuary_light.rs delete mode 100644 crates/theme2/src/themes/atelier_forest_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_forest_light.rs delete mode 100644 crates/theme2/src/themes/atelier_heath_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_heath_light.rs delete mode 100644 crates/theme2/src/themes/atelier_lakeside_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_lakeside_light.rs delete mode 100644 crates/theme2/src/themes/atelier_plateau_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_plateau_light.rs delete mode 100644 crates/theme2/src/themes/atelier_savanna_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_savanna_light.rs delete mode 100644 crates/theme2/src/themes/atelier_seaside_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_seaside_light.rs delete mode 100644 crates/theme2/src/themes/atelier_sulphurpool_dark.rs delete mode 100644 crates/theme2/src/themes/atelier_sulphurpool_light.rs delete mode 100644 crates/theme2/src/themes/ayu_dark.rs delete mode 100644 crates/theme2/src/themes/ayu_light.rs delete mode 100644 crates/theme2/src/themes/ayu_mirage.rs delete mode 100644 crates/theme2/src/themes/gruvbox_dark.rs delete mode 100644 crates/theme2/src/themes/gruvbox_dark_hard.rs delete mode 100644 crates/theme2/src/themes/gruvbox_dark_soft.rs delete mode 100644 crates/theme2/src/themes/gruvbox_light.rs delete mode 100644 crates/theme2/src/themes/gruvbox_light_hard.rs delete mode 100644 crates/theme2/src/themes/gruvbox_light_soft.rs delete mode 100644 crates/theme2/src/themes/mod.rs delete mode 100644 crates/theme2/src/themes/one_dark.rs delete mode 100644 crates/theme2/src/themes/one_light.rs delete mode 100644 crates/theme2/src/themes/rose_pine.rs delete mode 100644 crates/theme2/src/themes/rose_pine_dawn.rs delete mode 100644 crates/theme2/src/themes/rose_pine_moon.rs delete mode 100644 crates/theme2/src/themes/sandcastle.rs delete mode 100644 crates/theme2/src/themes/solarized_dark.rs delete mode 100644 crates/theme2/src/themes/solarized_light.rs delete mode 100644 crates/theme2/src/themes/summercamp.rs delete mode 100644 crates/theme_converter/Cargo.toml delete mode 100644 crates/theme_converter/src/main.rs delete mode 100644 crates/theme_converter/src/theme_printer.rs diff --git a/Cargo.lock b/Cargo.lock index 272320895d..5691729766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8769,21 +8769,6 @@ dependencies = [ "util", ] -[[package]] -name = "theme_converter" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 4.4.4", - "convert_case 0.6.0", - "gpui2", - "log", - "rust-embed", - "serde", - "simplelog", - "theme2", -] - [[package]] name = "theme_selector" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index ac490ce935..cb0e12cc40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,6 @@ members = [ "crates/text", "crates/theme", "crates/theme2", - "crates/theme_converter", "crates/theme_selector", "crates/ui2", "crates/util", diff --git a/crates/language2/src/language2.rs b/crates/language2/src/language2.rs index 717a80619b..21746bf43c 100644 --- a/crates/language2/src/language2.rs +++ b/crates/language2/src/language2.rs @@ -42,7 +42,7 @@ use std::{ }, }; use syntax_map::SyntaxSnapshot; -use theme2::{SyntaxTheme, Theme}; +use theme2::{SyntaxTheme, ThemeVariant}; use tree_sitter::{self, Query}; use unicase::UniCase; use util::{http::HttpClient, paths::PathExt}; @@ -642,7 +642,7 @@ struct LanguageRegistryState { next_available_language_id: AvailableLanguageId, loading_languages: HashMap>>>>, subscription: (watch::Sender<()>, watch::Receiver<()>), - theme: Option>, + theme: Option>, version: usize, reload_count: usize, } @@ -743,11 +743,11 @@ impl LanguageRegistry { self.state.read().reload_count } - pub fn set_theme(&self, theme: Arc) { + pub fn set_theme(&self, theme: Arc) { let mut state = self.state.write(); state.theme = Some(theme.clone()); for language in &state.languages { - language.set_theme(&theme.syntax); + language.set_theme(&theme.syntax()); } } @@ -1048,7 +1048,7 @@ impl LanguageRegistryState { fn add(&mut self, language: Arc) { if let Some(theme) = self.theme.as_ref() { - language.set_theme(&theme.syntax); + language.set_theme(&theme.syntax()); } self.languages.push(language); self.version += 1; diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 6028695d7f..411fe18071 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -48,7 +48,7 @@ fn main() { let args = Args::parse(); let story_selector = args.story.clone(); - let theme_name = args.theme.unwrap_or("One Dark".to_string()); + let theme_name = args.theme.unwrap_or("Zed Pro Moonlight".to_string()); let asset_source = Arc::new(Assets); gpui2::App::production(asset_source).run(move |cx| { @@ -68,7 +68,7 @@ fn main() { let theme_registry = cx.global::(); let mut theme_settings = ThemeSettings::get_global(cx).clone(); - theme_settings.old_active_theme = theme_registry.get(&theme_name).unwrap(); + theme_settings.active_theme = theme_registry.get(&theme_name).unwrap(); ThemeSettings::override_global(theme_settings, cx); ui::settings::init(cx); diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index d23fde1ee0..2a59fa41bd 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -1,7 +1,7 @@ use gpui2::Hsla; use refineable::Refineable; -use crate::{generate_struct_with_overrides, SyntaxStyles}; +use crate::{generate_struct_with_overrides, SyntaxTheme}; pub struct SystemColors { pub transparent: Hsla, @@ -94,7 +94,7 @@ generate_struct_with_overrides! { status: StatusColors, git: GitStatusColors, player: PlayerColors, - syntax: SyntaxStyles + syntax: SyntaxTheme } #[cfg(test)] diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index e8146cdeaa..5ef93d036f 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -1,11 +1,9 @@ -use gpui2::{hsla, FontWeight, Rgba}; -use indexmap::IndexMap; +use gpui2::{hsla, Rgba}; use crate::{ colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, scale::{ColorScaleSet, ColorScales}, - syntax::{SyntaxStyleName, SyntaxStyles}, - SyntaxStyle, + syntax::SyntaxTheme, }; impl Default for SystemColors { @@ -77,541 +75,115 @@ impl Default for PlayerColors { } } -impl SyntaxStyles { +impl SyntaxTheme { pub fn default_light() -> Self { - use SyntaxStyleName::*; - - let neutral: ColorScaleSet = slate().into(); - - Self(IndexMap::from_iter([ - ( - Comment, - SyntaxStyle::builder().color(neutral.light(11)).build(), - ), - ( - CommentDoc, - SyntaxStyle::builder().color(neutral.light(11)).build(), - ), - ( - Primary, - SyntaxStyle::builder().color(neutral.light(12)).build(), - ), - ( - Predictive, - SyntaxStyle::builder().color(neutral.light(10)).build(), - ), - ( - Hint, - SyntaxStyle::builder() - .color(ColorScaleSet::from(cyan()).light(10)) - .build(), - ), - ( - Emphasis, - SyntaxStyle::builder().weight(FontWeight(600.0)).build(), - ), - ( - EmphasisStrong, - SyntaxStyle::builder().weight(FontWeight(800.0)).build(), - ), - ( - Title, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - LinkUri, - SyntaxStyle::builder() - .color(ColorScaleSet::from(blue()).light(12)) - .build(), - ), - ( - LinkText, - SyntaxStyle::builder() - .color(ColorScaleSet::from(orange()).light(12)) - .build(), - ), - ( - TextLiteral, - SyntaxStyle::builder() - .color(ColorScaleSet::from(purple()).light(12)) - .build(), - ), - ( - Punctuation, - SyntaxStyle::builder().color(neutral.light(10)).build(), - ), - ( - PunctuationBracket, - SyntaxStyle::builder().color(neutral.light(10)).build(), - ), - ( - PunctuationDelimiter, - SyntaxStyle::builder().color(neutral.light(10)).build(), - ), - ( - PunctuationSpecial, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - PunctuationListMarker, - SyntaxStyle::builder() - .color(ColorScaleSet::from(blue()).light(12)) - .build(), - ), - ( - String, - SyntaxStyle::builder() - .color(ColorScaleSet::from(green()).light(12)) - .build(), - ), - ( - StringSpecial, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - StringSpecialSymbol, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - StringEscape, - SyntaxStyle::builder() - .color(ColorScaleSet::from(blue()).light(12)) - .build(), - ), - ( - StringRegex, - SyntaxStyle::builder() - .color(ColorScaleSet::from(orange()).light(12)) - .build(), - ), - ( - Constructor, - SyntaxStyle::builder() - .color(ColorScaleSet::from(purple()).light(12)) - .build(), - ), - // TODO: Continue assigning syntax colors from here - ( - Variant, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Type, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - TypeBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Variable, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - VariableSpecial, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Label, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Tag, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Attribute, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Property, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Constant, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Keyword, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Enum, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Operator, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Number, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Boolean, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - ConstantBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Function, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - FunctionBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - FunctionDefinition, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - FunctionSpecialDefinition, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - FunctionMethod, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - FunctionMethodBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Preproc, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ( - Embedded, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).light(12)) - .build(), - ), - ])) + Self { + highlights: vec![ + ( + "string.special.symbol".into(), + gpui2::rgba(0xad6e26ff).into(), + ), + ("hint".into(), gpui2::rgba(0x9294beff).into()), + ("link_uri".into(), gpui2::rgba(0x3882b7ff).into()), + ("type".into(), gpui2::rgba(0x3882b7ff).into()), + ("string.regex".into(), gpui2::rgba(0xad6e26ff).into()), + ("constant".into(), gpui2::rgba(0x669f59ff).into()), + ("function".into(), gpui2::rgba(0x5b79e3ff).into()), + ("string.special".into(), gpui2::rgba(0xad6e26ff).into()), + ("punctuation.bracket".into(), gpui2::rgba(0x4d4f52ff).into()), + ("variable".into(), gpui2::rgba(0x383a41ff).into()), + ("punctuation".into(), gpui2::rgba(0x383a41ff).into()), + ("property".into(), gpui2::rgba(0xd3604fff).into()), + ("string".into(), gpui2::rgba(0x649f57ff).into()), + ("predictive".into(), gpui2::rgba(0x9b9ec6ff).into()), + ("attribute".into(), gpui2::rgba(0x5c78e2ff).into()), + ("number".into(), gpui2::rgba(0xad6e25ff).into()), + ("constructor".into(), gpui2::rgba(0x5c78e2ff).into()), + ("embedded".into(), gpui2::rgba(0x383a41ff).into()), + ("title".into(), gpui2::rgba(0xd3604fff).into()), + ("tag".into(), gpui2::rgba(0x5c78e2ff).into()), + ("boolean".into(), gpui2::rgba(0xad6e25ff).into()), + ( + "punctuation.list_marker".into(), + gpui2::rgba(0xd3604fff).into(), + ), + ("variant".into(), gpui2::rgba(0x5b79e3ff).into()), + ("emphasis".into(), gpui2::rgba(0x5c78e2ff).into()), + ("link_text".into(), gpui2::rgba(0x5b79e3ff).into()), + ("comment".into(), gpui2::rgba(0xa2a3a7ff).into()), + ("punctuation.special".into(), gpui2::rgba(0xb92b46ff).into()), + ("emphasis.strong".into(), gpui2::rgba(0xad6e25ff).into()), + ("primary".into(), gpui2::rgba(0x383a41ff).into()), + ( + "punctuation.delimiter".into(), + gpui2::rgba(0x4d4f52ff).into(), + ), + ("label".into(), gpui2::rgba(0x5c78e2ff).into()), + ("keyword".into(), gpui2::rgba(0xa449abff).into()), + ("string.escape".into(), gpui2::rgba(0x7c7e86ff).into()), + ("text.literal".into(), gpui2::rgba(0x649f57ff).into()), + ("variable.special".into(), gpui2::rgba(0xad6e25ff).into()), + ("comment.doc".into(), gpui2::rgba(0x7c7e86ff).into()), + ("enum".into(), gpui2::rgba(0xd3604fff).into()), + ("operator".into(), gpui2::rgba(0x3882b7ff).into()), + ("preproc".into(), gpui2::rgba(0x383a41ff).into()), + ], + } } pub fn default_dark() -> Self { - use SyntaxStyleName::*; - - let neutral: ColorScaleSet = slate().into(); - - Self(IndexMap::from_iter([ - ( - Comment, - SyntaxStyle::builder().color(neutral.dark(11)).build(), - ), - ( - CommentDoc, - SyntaxStyle::builder().color(neutral.dark(11)).build(), - ), - ( - Primary, - SyntaxStyle::builder().color(neutral.dark(12)).build(), - ), - ( - Predictive, - SyntaxStyle::builder().color(neutral.dark(10)).build(), - ), - ( - Hint, - SyntaxStyle::builder() - .color(ColorScaleSet::from(cyan()).dark(10)) - .build(), - ), - ( - Emphasis, - SyntaxStyle::builder().weight(FontWeight(600.0)).build(), - ), - ( - EmphasisStrong, - SyntaxStyle::builder().weight(FontWeight(800.0)).build(), - ), - ( - Title, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - LinkUri, - SyntaxStyle::builder() - .color(ColorScaleSet::from(blue()).dark(12)) - .build(), - ), - ( - LinkText, - SyntaxStyle::builder() - .color(ColorScaleSet::from(orange()).dark(12)) - .build(), - ), - ( - TextLiteral, - SyntaxStyle::builder() - .color(ColorScaleSet::from(purple()).dark(12)) - .build(), - ), - ( - Punctuation, - SyntaxStyle::builder().color(neutral.dark(10)).build(), - ), - ( - PunctuationBracket, - SyntaxStyle::builder().color(neutral.dark(10)).build(), - ), - ( - PunctuationDelimiter, - SyntaxStyle::builder().color(neutral.dark(10)).build(), - ), - ( - PunctuationSpecial, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - PunctuationListMarker, - SyntaxStyle::builder() - .color(ColorScaleSet::from(blue()).dark(12)) - .build(), - ), - ( - String, - SyntaxStyle::builder() - .color(ColorScaleSet::from(green()).dark(12)) - .build(), - ), - ( - StringSpecial, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - StringSpecialSymbol, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - StringEscape, - SyntaxStyle::builder() - .color(ColorScaleSet::from(blue()).dark(12)) - .build(), - ), - ( - StringRegex, - SyntaxStyle::builder() - .color(ColorScaleSet::from(orange()).dark(12)) - .build(), - ), - ( - Constructor, - SyntaxStyle::builder() - .color(ColorScaleSet::from(purple()).dark(12)) - .build(), - ), - // TODO: Continue assigning syntax colors from here - ( - Variant, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Type, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - TypeBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Variable, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - VariableSpecial, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Label, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Tag, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Attribute, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Property, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Constant, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Keyword, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Enum, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Operator, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Number, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Boolean, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - ConstantBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Function, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - FunctionBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - FunctionDefinition, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - FunctionSpecialDefinition, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - FunctionMethod, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - FunctionMethodBuiltin, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Preproc, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ( - Embedded, - SyntaxStyle::builder() - .color(ColorScaleSet::from(red()).dark(12)) - .build(), - ), - ])) + Self { + highlights: vec![ + ("keyword".into(), gpui2::rgba(0xb477cfff).into()), + ("comment.doc".into(), gpui2::rgba(0x878e98ff).into()), + ("variant".into(), gpui2::rgba(0x73ade9ff).into()), + ("property".into(), gpui2::rgba(0xd07277ff).into()), + ("function".into(), gpui2::rgba(0x73ade9ff).into()), + ("type".into(), gpui2::rgba(0x6eb4bfff).into()), + ("tag".into(), gpui2::rgba(0x74ade8ff).into()), + ("string.escape".into(), gpui2::rgba(0x878e98ff).into()), + ("punctuation.bracket".into(), gpui2::rgba(0xb2b9c6ff).into()), + ("hint".into(), gpui2::rgba(0x5a6f89ff).into()), + ("punctuation".into(), gpui2::rgba(0xacb2beff).into()), + ("comment".into(), gpui2::rgba(0x5d636fff).into()), + ("emphasis".into(), gpui2::rgba(0x74ade8ff).into()), + ("punctuation.special".into(), gpui2::rgba(0xb1574bff).into()), + ("link_uri".into(), gpui2::rgba(0x6eb4bfff).into()), + ("string.regex".into(), gpui2::rgba(0xbf956aff).into()), + ("constructor".into(), gpui2::rgba(0x73ade9ff).into()), + ("operator".into(), gpui2::rgba(0x6eb4bfff).into()), + ("constant".into(), gpui2::rgba(0xdfc184ff).into()), + ("string.special".into(), gpui2::rgba(0xbf956aff).into()), + ("emphasis.strong".into(), gpui2::rgba(0xbf956aff).into()), + ( + "string.special.symbol".into(), + gpui2::rgba(0xbf956aff).into(), + ), + ("primary".into(), gpui2::rgba(0xacb2beff).into()), + ("preproc".into(), gpui2::rgba(0xc8ccd4ff).into()), + ("string".into(), gpui2::rgba(0xa1c181ff).into()), + ( + "punctuation.delimiter".into(), + gpui2::rgba(0xb2b9c6ff).into(), + ), + ("embedded".into(), gpui2::rgba(0xc8ccd4ff).into()), + ("enum".into(), gpui2::rgba(0xd07277ff).into()), + ("variable.special".into(), gpui2::rgba(0xbf956aff).into()), + ("text.literal".into(), gpui2::rgba(0xa1c181ff).into()), + ("attribute".into(), gpui2::rgba(0x74ade8ff).into()), + ("link_text".into(), gpui2::rgba(0x73ade9ff).into()), + ("title".into(), gpui2::rgba(0xd07277ff).into()), + ("predictive".into(), gpui2::rgba(0x5a6a87ff).into()), + ("number".into(), gpui2::rgba(0xbf956aff).into()), + ("label".into(), gpui2::rgba(0x74ade8ff).into()), + ("variable".into(), gpui2::rgba(0xc8ccd4ff).into()), + ("boolean".into(), gpui2::rgba(0xbf956aff).into()), + ( + "punctuation.list_marker".into(), + gpui2::rgba(0xd07277ff).into(), + ), + ], + } } } diff --git a/crates/theme2/src/default_theme.rs b/crates/theme2/src/default_theme.rs index 4b47e403d6..26a55b5e0d 100644 --- a/crates/theme2/src/default_theme.rs +++ b/crates/theme2/src/default_theme.rs @@ -1,12 +1,12 @@ use crate::{ colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyle}, - default_color_scales, Appearance, SyntaxStyles, ThemeFamily, ThemeVariant, + default_color_scales, Appearance, SyntaxTheme, ThemeFamily, ThemeVariant, }; fn zed_pro_daylight() -> ThemeVariant { ThemeVariant { id: "zed_pro_daylight".to_string(), - name: "Zed Pro Daylight".to_string(), + name: "Zed Pro Daylight".into(), appearance: Appearance::Light, styles: ThemeStyle { system: SystemColors::default(), @@ -14,7 +14,7 @@ fn zed_pro_daylight() -> ThemeVariant { status: StatusColors::default(), git: GitStatusColors::default(), player: PlayerColors::default(), - syntax: SyntaxStyles::default_light(), + syntax: SyntaxTheme::default_light(), }, } } @@ -22,15 +22,15 @@ fn zed_pro_daylight() -> ThemeVariant { pub(crate) fn zed_pro_moonlight() -> ThemeVariant { ThemeVariant { id: "zed_pro_moonlight".to_string(), - name: "Zed Pro Moonlight".to_string(), - appearance: Appearance::Light, + name: "Zed Pro Moonlight".into(), + appearance: Appearance::Dark, styles: ThemeStyle { system: SystemColors::default(), colors: ThemeColors::default_dark(), status: StatusColors::default(), git: GitStatusColors::default(), player: PlayerColors::default(), - syntax: SyntaxStyles::default_dark(), + syntax: SyntaxTheme::default_dark(), }, } } @@ -38,8 +38,8 @@ pub(crate) fn zed_pro_moonlight() -> ThemeVariant { pub fn zed_pro_family() -> ThemeFamily { ThemeFamily { id: "zed_pro".to_string(), - name: "Zed Pro".to_string(), - author: "Zed Team".to_string(), + name: "Zed Pro".into(), + author: "Zed Team".into(), themes: vec![zed_pro_daylight(), zed_pro_moonlight()], scales: default_color_scales(), } diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index eec82ef5a7..f30f5ead91 100644 --- a/crates/theme2/src/registry.rs +++ b/crates/theme2/src/registry.rs @@ -1,17 +1,22 @@ -use crate::{themes, Theme, ThemeMetadata}; +use crate::{zed_pro_family, ThemeFamily, ThemeVariant}; use anyhow::{anyhow, Result}; use gpui2::SharedString; use std::{collections::HashMap, sync::Arc}; pub struct ThemeRegistry { - themes: HashMap>, + themes: HashMap>, } impl ThemeRegistry { - fn insert_themes(&mut self, themes: impl IntoIterator) { + fn insert_theme_families(&mut self, families: impl IntoIterator) { + for family in families.into_iter() { + self.insert_themes(family.themes); + } + } + + fn insert_themes(&mut self, themes: impl IntoIterator) { for theme in themes.into_iter() { - self.themes - .insert(theme.metadata.name.clone(), Arc::new(theme)); + self.themes.insert(theme.name.clone(), Arc::new(theme)); } } @@ -19,11 +24,11 @@ impl ThemeRegistry { self.themes.keys().cloned() } - pub fn list(&self, _staff: bool) -> impl Iterator + '_ { - self.themes.values().map(|theme| theme.metadata.clone()) + pub fn list(&self, _staff: bool) -> impl Iterator + '_ { + self.themes.values().map(|theme| theme.name.clone()) } - pub fn get(&self, name: &str) -> Result> { + pub fn get(&self, name: &str) -> Result> { self.themes .get(name) .ok_or_else(|| anyhow!("theme not found: {}", name)) @@ -37,47 +42,7 @@ impl Default for ThemeRegistry { themes: HashMap::default(), }; - this.insert_themes([ - 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.insert_theme_families([zed_pro_family()]); this } diff --git a/crates/theme2/src/settings.rs b/crates/theme2/src/settings.rs index 3a61bbbe1e..bad00ee196 100644 --- a/crates/theme2/src/settings.rs +++ b/crates/theme2/src/settings.rs @@ -1,4 +1,4 @@ -use crate::{zed_pro_moonlight, Theme, ThemeRegistry, ThemeVariant}; +use crate::{ThemeRegistry, ThemeVariant}; use anyhow::Result; use gpui2::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels}; use schemars::{ @@ -21,7 +21,6 @@ pub struct ThemeSettings { pub buffer_font_size: Pixels, pub buffer_line_height: BufferLineHeight, pub active_theme: Arc, - pub old_active_theme: Arc, } #[derive(Default)] @@ -124,8 +123,9 @@ impl settings2::Settings for ThemeSettings { }, buffer_font_size: defaults.buffer_font_size.unwrap().into(), buffer_line_height: defaults.buffer_line_height.unwrap(), - active_theme: Arc::new(zed_pro_moonlight()), - old_active_theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), + active_theme: themes.get("Zed Pro Moonlight").unwrap(), + // todo!(Read the theme name from the settings) + // active_theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), }; for value in user_values.into_iter().copied().cloned() { @@ -138,7 +138,7 @@ impl settings2::Settings for ThemeSettings { if let Some(value) = &value.theme { if let Some(theme) = themes.get(value).log_err() { - this.old_active_theme = theme; + this.active_theme = theme; } } diff --git a/crates/theme2/src/syntax.rs b/crates/theme2/src/syntax.rs index 82c4c87796..1cf8564bca 100644 --- a/crates/theme2/src/syntax.rs +++ b/crates/theme2/src/syntax.rs @@ -1,227 +1,37 @@ -use gpui2::{FontWeight, Hsla, SharedString}; -use indexmap::IndexMap; +use gpui2::{HighlightStyle, Hsla}; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum SyntaxStyleName { - Comment, - CommentDoc, - Primary, - Predictive, - Hint, - Emphasis, - EmphasisStrong, - Title, - LinkUri, - LinkText, - TextLiteral, - Punctuation, - PunctuationBracket, - PunctuationDelimiter, - PunctuationSpecial, - PunctuationListMarker, - String, - StringSpecial, - StringSpecialSymbol, - StringEscape, - StringRegex, - Constructor, - Variant, - Type, - TypeBuiltin, - Variable, - VariableSpecial, - Label, - Tag, - Attribute, - Property, - Constant, - Keyword, - Enum, - Operator, - Number, - Boolean, - ConstantBuiltin, - Function, - FunctionBuiltin, - FunctionDefinition, - FunctionSpecialDefinition, - FunctionMethod, - FunctionMethodBuiltin, - Preproc, - Embedded, - Custom(SharedString), +#[derive(Clone)] +pub struct SyntaxTheme { + pub highlights: Vec<(String, HighlightStyle)>, } -impl std::str::FromStr for SyntaxStyleName { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "attribute" => Self::Attribute, - "boolean" => Self::Boolean, - "comment" => Self::Comment, - "comment.doc" => Self::CommentDoc, - "constant" => Self::Constant, - "constructor" => Self::Constructor, - "embedded" => Self::Embedded, - "emphasis" => Self::Emphasis, - "emphasis.strong" => Self::EmphasisStrong, - "enum" => Self::Enum, - "function" => Self::Function, - "function.builtin" => Self::FunctionBuiltin, - "function.definition" => Self::FunctionDefinition, - "function.special_definition" => Self::FunctionSpecialDefinition, - "function.method" => Self::FunctionMethod, - "function.method_builtin" => Self::FunctionMethodBuiltin, - "hint" => Self::Hint, - "keyword" => Self::Keyword, - "label" => Self::Label, - "link_text" => Self::LinkText, - "link_uri" => Self::LinkUri, - "number" => Self::Number, - "operator" => Self::Operator, - "predictive" => Self::Predictive, - "preproc" => Self::Preproc, - "primary" => Self::Primary, - "property" => Self::Property, - "punctuation" => Self::Punctuation, - "punctuation.bracket" => Self::PunctuationBracket, - "punctuation.delimiter" => Self::PunctuationDelimiter, - "punctuation.list_marker" => Self::PunctuationListMarker, - "punctuation.special" => Self::PunctuationSpecial, - "string" => Self::String, - "string.escape" => Self::StringEscape, - "string.regex" => Self::StringRegex, - "string.special" => Self::StringSpecial, - "string.special.symbol" => Self::StringSpecialSymbol, - "tag" => Self::Tag, - "text.literal" => Self::TextLiteral, - "title" => Self::Title, - "type" => Self::Type, - "type.builtin" => Self::TypeBuiltin, - "variable" => Self::Variable, - "variable.special" => Self::VariableSpecial, - "constant.builtin" => Self::ConstantBuiltin, - "variant" => Self::Variant, - name => Self::Custom(name.to_string().into()), - }) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct SyntaxStyle { - pub color: Hsla, - pub weight: FontWeight, - pub underline: bool, - pub italic: bool, - // Nate: In the future I'd like to enable using background highlights for syntax highlighting - // pub highlight: Hsla, -} - -impl SyntaxStyle { - pub fn builder() -> SyntaxStyleBuilder { - SyntaxStyleBuilder::new() - } -} - -impl Default for SyntaxStyle { - fn default() -> Self { - Self { - color: gpui2::black(), - weight: FontWeight::default(), - italic: false, - underline: false, - } - } -} - -pub struct SyntaxStyleBuilder { - pub color: Hsla, - pub weight: FontWeight, - pub underline: bool, - pub italic: bool, -} - -impl SyntaxStyleBuilder { - pub fn new() -> Self { - SyntaxStyleBuilder { - color: gpui2::black(), - weight: FontWeight::default(), - underline: false, - italic: false, - } - } - - pub fn color(mut self, color: Hsla) -> Self { - self.color = color; - self - } - - pub fn weight(mut self, weight: FontWeight) -> Self { - self.weight = weight; - self - } - - pub fn underline(mut self, underline: bool) -> Self { - self.underline = underline; - self - } - - pub fn italic(mut self, italic: bool) -> Self { - self.italic = italic; - self - } - - pub fn build(self) -> SyntaxStyle { - SyntaxStyle { - color: self.color, - weight: self.weight, - underline: self.underline, - italic: self.italic, - } - } -} - -pub struct SyntaxStyles(pub IndexMap); - -impl SyntaxStyles { +impl SyntaxTheme { // TOOD: Get this working with `#[cfg(test)]`. Why isn't it? pub fn new_test(colors: impl IntoIterator) -> Self { - Self(IndexMap::from_iter(colors.into_iter().map( - |(name, color)| { - ( - name.parse().unwrap(), - SyntaxStyle::builder().color(color).build(), - ) - }, - ))) + SyntaxTheme { + highlights: colors + .into_iter() + .map(|(key, color)| { + ( + key.to_owned(), + HighlightStyle { + color: Some(color), + ..Default::default() + }, + ) + }) + .collect(), + } } - pub fn get(&self, name: &str) -> SyntaxStyle { - self.0 - .get(&name.parse::().unwrap()) - .cloned() + pub fn get(&self, name: &str) -> HighlightStyle { + self.highlights + .iter() + .find_map(|entry| if entry.0 == name { Some(entry.1) } else { None }) .unwrap_or_default() } pub fn color(&self, name: &str) -> Hsla { - self.get(name).color - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_syntax_style_name() { - let name = "comment".parse::().unwrap(); - assert_eq!(name, SyntaxStyleName::Comment); - } - - #[test] - fn create_custom_syntax_style_name() { - let name = "custom".parse::().unwrap(); - assert_eq!(name, SyntaxStyleName::Custom("custom".into())); + self.get(name).color.unwrap_or_default() } } diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index ea1ad5b26c..372e976bd3 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -5,7 +5,6 @@ mod registry; mod scale; mod settings; mod syntax; -mod themes; mod utils; pub use colors::*; @@ -16,9 +15,7 @@ pub use scale::*; pub use settings::*; pub use syntax::*; -use std::sync::Arc; - -use gpui2::{AppContext, HighlightStyle, Hsla, SharedString}; +use gpui2::{AppContext, Hsla, SharedString}; use settings2::Settings; #[derive(Debug, Clone, PartialEq)] @@ -45,8 +42,8 @@ impl ActiveTheme for AppContext { pub struct ThemeFamily { #[allow(dead_code)] pub(crate) id: String, - pub name: String, - pub author: String, + pub name: SharedString, + pub author: SharedString, pub themes: Vec, pub scales: ColorScales, } @@ -56,7 +53,7 @@ impl ThemeFamily {} pub struct ThemeVariant { #[allow(dead_code)] pub(crate) id: String, - pub name: String, + pub name: SharedString, pub appearance: Appearance, pub styles: ThemeStyle, } @@ -68,9 +65,9 @@ impl ThemeVariant { &self.styles.colors } - /// Returns the [`SyntaxStyles`] for the theme. + /// Returns the [`SyntaxTheme`] for the theme. #[inline(always)] - pub fn syntax(&self) -> &SyntaxStyles { + pub fn syntax(&self) -> &SyntaxTheme { &self.styles.syntax } @@ -80,125 +77,3 @@ impl ThemeVariant { self.syntax().color(name) } } - -pub struct Theme { - pub metadata: ThemeMetadata, - - pub transparent: Hsla, - pub mac_os_traffic_light_red: Hsla, - pub mac_os_traffic_light_yellow: Hsla, - pub mac_os_traffic_light_green: Hsla, - pub border: Hsla, - pub border_variant: Hsla, - pub border_focused: Hsla, - pub border_transparent: Hsla, - /// The background color of an elevated surface, like a modal, tooltip or toast. - pub elevated_surface: Hsla, - pub surface: Hsla, - /// Window background color of the base app - pub background: Hsla, - /// Default background for elements like filled buttons, - /// text fields, checkboxes, radio buttons, etc. - /// - TODO: Map to step 3. - pub filled_element: Hsla, - /// The background color of a hovered element, like a button being hovered - /// with a mouse, or hovered on a touch screen. - /// - TODO: Map to step 4. - pub filled_element_hover: Hsla, - /// The background color of an active element, like a button being pressed, - /// or tapped on a touch screen. - /// - TODO: Map to step 5. - pub filled_element_active: Hsla, - /// The background color of a selected element, like a selected tab, - /// a button toggled on, or a checkbox that is checked. - pub filled_element_selected: Hsla, - pub filled_element_disabled: Hsla, - pub ghost_element: Hsla, - /// The background color of a hovered element with no default background, - /// like a ghost-style button or an interactable list item. - /// - TODO: Map to step 3. - pub ghost_element_hover: Hsla, - /// - TODO: Map to step 4. - pub ghost_element_active: Hsla, - pub ghost_element_selected: Hsla, - pub ghost_element_disabled: Hsla, - pub text: Hsla, - pub text_muted: Hsla, - pub text_placeholder: Hsla, - pub text_disabled: Hsla, - pub text_accent: Hsla, - pub icon_muted: Hsla, - pub syntax: SyntaxTheme, - - pub status_bar: Hsla, - pub title_bar: Hsla, - pub toolbar: Hsla, - pub tab_bar: Hsla, - /// The background of the editor - pub editor: Hsla, - pub editor_subheader: Hsla, - pub editor_active_line: Hsla, - pub terminal: Hsla, - pub image_fallback_background: Hsla, - - pub git_created: Hsla, - pub git_modified: Hsla, - pub git_deleted: Hsla, - pub git_conflict: Hsla, - pub git_ignored: Hsla, - pub git_renamed: Hsla, - - pub players: [PlayerTheme; 8], -} - -#[derive(Clone)] -pub struct SyntaxTheme { - pub highlights: Vec<(String, HighlightStyle)>, -} - -impl SyntaxTheme { - // TOOD: Get this working with `#[cfg(test)]`. Why isn't it? - pub fn new_test(colors: impl IntoIterator) -> Self { - SyntaxTheme { - highlights: colors - .into_iter() - .map(|(key, color)| { - ( - key.to_owned(), - HighlightStyle { - color: Some(color), - ..Default::default() - }, - ) - }) - .collect(), - } - } - - pub fn get(&self, name: &str) -> HighlightStyle { - self.highlights - .iter() - .find_map(|entry| if entry.0 == name { Some(entry.1) } else { None }) - .unwrap_or_default() - } - - pub fn color(&self, name: &str) -> Hsla { - self.get(name).color.unwrap_or_default() - } -} - -#[derive(Clone, Copy)] -pub struct PlayerTheme { - pub cursor: Hsla, - pub selection: Hsla, -} - -#[derive(Clone)] -pub struct ThemeMetadata { - pub name: SharedString, - pub is_light: bool, -} - -pub struct Editor { - pub syntax: Arc, -} diff --git a/crates/theme2/src/themes/andromeda.rs b/crates/theme2/src/themes/andromeda.rs deleted file mode 100644 index 6afd7edd4d..0000000000 --- a/crates/theme2/src/themes/andromeda.rs +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index c5190f4e98..0000000000 --- a/crates/theme2/src/themes/atelier_cave_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index ae2e912f14..0000000000 --- a/crates/theme2/src/themes/atelier_cave_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 03d0c5eea0..0000000000 --- a/crates/theme2/src/themes/atelier_dune_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 1d0f944916..0000000000 --- a/crates/theme2/src/themes/atelier_dune_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index ad5c9fbc1e..0000000000 --- a/crates/theme2/src/themes/atelier_estuary_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 91eaa88fab..0000000000 --- a/crates/theme2/src/themes/atelier_estuary_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 83228e671f..0000000000 --- a/crates/theme2/src/themes/atelier_forest_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 882d5c2fcb..0000000000 --- a/crates/theme2/src/themes/atelier_forest_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 354c98069f..0000000000 --- a/crates/theme2/src/themes/atelier_heath_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index f1a9e4d8c6..0000000000 --- a/crates/theme2/src/themes/atelier_heath_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 61b78864b7..0000000000 --- a/crates/theme2/src/themes/atelier_lakeside_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 64fb70dadb..0000000000 --- a/crates/theme2/src/themes/atelier_lakeside_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 0ba5a1659d..0000000000 --- a/crates/theme2/src/themes/atelier_plateau_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 68f100dd85..0000000000 --- a/crates/theme2/src/themes/atelier_plateau_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index d4040db958..0000000000 --- a/crates/theme2/src/themes/atelier_savanna_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 08722cd91c..0000000000 --- a/crates/theme2/src/themes/atelier_savanna_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 475115e0d1..0000000000 --- a/crates/theme2/src/themes/atelier_seaside_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 557134b540..0000000000 --- a/crates/theme2/src/themes/atelier_seaside_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 8be8451740..0000000000 --- a/crates/theme2/src/themes/atelier_sulphurpool_dark.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index dba723331a..0000000000 --- a/crates/theme2/src/themes/atelier_sulphurpool_light.rs +++ /dev/null @@ -1,136 +0,0 @@ -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 deleted file mode 100644 index 35d3a43154..0000000000 --- a/crates/theme2/src/themes/ayu_dark.rs +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index 887282e564..0000000000 --- a/crates/theme2/src/themes/ayu_light.rs +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index 2974881a18..0000000000 --- a/crates/theme2/src/themes/ayu_mirage.rs +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index 6e982808cf..0000000000 --- a/crates/theme2/src/themes/gruvbox_dark.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index 159ab28325..0000000000 --- a/crates/theme2/src/themes/gruvbox_dark_hard.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index 6a6423389e..0000000000 --- a/crates/theme2/src/themes/gruvbox_dark_soft.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index 7582f8bd8a..0000000000 --- a/crates/theme2/src/themes/gruvbox_light.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index e5e3fe54cf..0000000000 --- a/crates/theme2/src/themes/gruvbox_light_hard.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index 15574e2960..0000000000 --- a/crates/theme2/src/themes/gruvbox_light_soft.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index 17cd5ac6e0..0000000000 --- a/crates/theme2/src/themes/mod.rs +++ /dev/null @@ -1,79 +0,0 @@ -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 deleted file mode 100644 index c7408d1820..0000000000 --- a/crates/theme2/src/themes/one_dark.rs +++ /dev/null @@ -1,131 +0,0 @@ -use gpui2::rgba; - -use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; - -pub fn one_dark() -> Theme { - 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![ - ("keyword".into(), rgba(0xb477cfff).into()), - ("comment.doc".into(), rgba(0x878e98ff).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()), - ("string.escape".into(), rgba(0x878e98ff).into()), - ("punctuation.bracket".into(), rgba(0xb2b9c6ff).into()), - ("hint".into(), rgba(0x5a6f89ff).into()), - ("punctuation".into(), rgba(0xacb2beff).into()), - ("comment".into(), rgba(0x5d636fff).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(), - 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/one_light.rs b/crates/theme2/src/themes/one_light.rs deleted file mode 100644 index ee802d57d3..0000000000 --- a/crates/theme2/src/themes/one_light.rs +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index f3bd454cdc..0000000000 --- a/crates/theme2/src/themes/rose_pine.rs +++ /dev/null @@ -1,132 +0,0 @@ -use gpui2::rgba; - -use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; - -pub fn rose_pine() -> Theme { - Theme { - metadata: ThemeMetadata { - name: "Rosé Pine".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(0x423f55ff).into(), - border_variant: rgba(0x423f55ff).into(), - border_focused: rgba(0x435255ff).into(), - border_transparent: rgba(0x00000000).into(), - elevated_surface: rgba(0x292738ff).into(), - surface: rgba(0x1c1b2aff).into(), - background: rgba(0x292738ff).into(), - filled_element: rgba(0x292738ff).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(0x74708dff).into(), - text_placeholder: rgba(0xea6e92ff).into(), - text_disabled: rgba(0x2f2b43ff).into(), - text_accent: rgba(0x9bced6ff).into(), - icon_muted: rgba(0x74708dff).into(), - syntax: SyntaxTheme { - highlights: vec![ - ("punctuation.delimiter".into(), rgba(0x9d99b6ff).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(), - title_bar: rgba(0x292738ff).into(), - toolbar: rgba(0x191724ff).into(), - tab_bar: rgba(0x1c1b2aff).into(), - editor: rgba(0x191724ff).into(), - editor_subheader: rgba(0x1c1b2aff).into(), - editor_active_line: rgba(0x1c1b2aff).into(), - terminal: rgba(0x191724ff).into(), - image_fallback_background: rgba(0x292738ff).into(), - git_created: rgba(0x5cc1a3ff).into(), - git_modified: rgba(0x9bced6ff).into(), - git_deleted: rgba(0xea6e92ff).into(), - git_conflict: rgba(0xf5c177ff).into(), - git_ignored: rgba(0x2f2b43ff).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(0x9d7591ff).into(), - selection: rgba(0x9d75913d).into(), - }, - PlayerTheme { - cursor: rgba(0xc4a7e6ff).into(), - selection: rgba(0xc4a7e63d).into(), - }, - PlayerTheme { - cursor: rgba(0xc4a7e6ff).into(), - selection: rgba(0xc4a7e63d).into(), - }, - PlayerTheme { - cursor: rgba(0x31738fff).into(), - selection: rgba(0x31738f3d).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 deleted file mode 100644 index ba64bf9d99..0000000000 --- a/crates/theme2/src/themes/rose_pine_dawn.rs +++ /dev/null @@ -1,132 +0,0 @@ -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 deleted file mode 100644 index 167b78afb5..0000000000 --- a/crates/theme2/src/themes/rose_pine_moon.rs +++ /dev/null @@ -1,132 +0,0 @@ -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 deleted file mode 100644 index 7fa0a27fb3..0000000000 --- a/crates/theme2/src/themes/sandcastle.rs +++ /dev/null @@ -1,130 +0,0 @@ -use gpui2::rgba; - -use crate::{PlayerTheme, SyntaxTheme, Theme, ThemeMetadata}; - -pub fn sandcastle() -> Theme { - 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![ - ("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()), - ("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()), - ("punctuation.special".into(), rgba(0xd5c5a1ff).into()), - ("keyword".into(), rgba(0x518b8bff).into()), - ("constructor".into(), rgba(0x518b8bff).into()), - ("predictive".into(), rgba(0x5c6152ff).into()), - ("title".into(), rgba(0xfdf4c1ff).into()), - ("variable".into(), rgba(0xfdf4c1ff).into()), - ("emphasis.strong".into(), rgba(0x518b8bff).into()), - ("primary".into(), rgba(0xfdf4c1ff).into()), - ("emphasis".into(), rgba(0x518b8bff).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(), - 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(), - }, - ], - } -} diff --git a/crates/theme2/src/themes/solarized_dark.rs b/crates/theme2/src/themes/solarized_dark.rs deleted file mode 100644 index 2e381a6e95..0000000000 --- a/crates/theme2/src/themes/solarized_dark.rs +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index a959a0a9d1..0000000000 --- a/crates/theme2/src/themes/solarized_light.rs +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index c1e66aedd1..0000000000 --- a/crates/theme2/src/themes/summercamp.rs +++ /dev/null @@ -1,130 +0,0 @@ -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(), - }, - ], - } -} diff --git a/crates/theme_converter/Cargo.toml b/crates/theme_converter/Cargo.toml deleted file mode 100644 index 0ec692b7cc..0000000000 --- a/crates/theme_converter/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "theme_converter" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[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 -serde.workspace = true -simplelog = "0.9" -theme2 = { path = "../theme2" } diff --git a/crates/theme_converter/src/main.rs b/crates/theme_converter/src/main.rs deleted file mode 100644 index cc0cdf9c99..0000000000 --- a/crates/theme_converter/src/main.rs +++ /dev/null @@ -1,390 +0,0 @@ -mod theme_printer; - -use std::borrow::Cow; -use std::collections::HashMap; -use std::fmt::{self, Debug}; -use std::fs::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; -use serde::de::Visitor; -use serde::{Deserialize, Deserializer}; -use simplelog::SimpleLogger; -use theme2::{PlayerTheme, SyntaxTheme}; - -use crate::theme_printer::ThemePrinter; - -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -struct Args { - /// The name of the theme to convert. - theme: String, -} - -fn main() -> Result<()> { - SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - - // let args = Args::parse(); - - 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(); - - 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() - .replace("é", "e") - .to_case(Case::Snake); - - let mut output_file = File::create(themes_path.join(format!("{theme_slug}.rs")))?; - - 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())?; - - 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(()) -} - -#[derive(RustEmbed)] -#[folder = "../../assets"] -#[include = "fonts/**/*"] -#[include = "icons/**/*"] -#[include = "themes/**/*"] -#[include = "sounds/**/*"] -#[include = "*.md"] -#[exclude = "*.DS_Store"] -pub struct Assets; - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result> { - Self::get(path) - .map(|f| f.data) - .ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path)) - } - - fn list(&self, path: &str) -> Result> { - Ok(Self::iter() - .filter(|p| p.starts_with(path)) - .map(SharedString::from) - .collect()) - } -} - -#[derive(Clone, Copy)] -pub struct PlayerThemeColors { - pub cursor: Hsla, - pub selection: Hsla, -} - -impl PlayerThemeColors { - pub fn new(theme: &LegacyTheme, ix: usize) -> Self { - if ix < theme.players.len() { - Self { - cursor: theme.players[ix].cursor, - selection: theme.players[ix].selection, - } - } else { - Self { - cursor: rgb::(0xff00ff), - selection: rgb::(0xff00ff), - } - } - } -} - -impl From for PlayerTheme { - fn from(value: PlayerThemeColors) -> Self { - Self { - cursor: value.cursor, - selection: value.selection, - } - } -} - -fn convert_theme(json_theme: JsonTheme, legacy_theme: LegacyTheme) -> Result { - let transparent = hsla(0.0, 0.0, 0.0, 0.0); - - let players: [PlayerTheme; 8] = [ - PlayerThemeColors::new(&legacy_theme, 0).into(), - PlayerThemeColors::new(&legacy_theme, 1).into(), - PlayerThemeColors::new(&legacy_theme, 2).into(), - PlayerThemeColors::new(&legacy_theme, 3).into(), - PlayerThemeColors::new(&legacy_theme, 4).into(), - PlayerThemeColors::new(&legacy_theme, 5).into(), - PlayerThemeColors::new(&legacy_theme, 6).into(), - PlayerThemeColors::new(&legacy_theme, 7).into(), - ]; - - let theme = theme2::Theme { - metadata: theme2::ThemeMetadata { - name: legacy_theme.name.clone().into(), - is_light: legacy_theme.is_light, - }, - transparent, - mac_os_traffic_light_red: rgb::(0xEC695E), - mac_os_traffic_light_yellow: rgb::(0xF4BF4F), - mac_os_traffic_light_green: rgb::(0x62C554), - border: legacy_theme.lowest.base.default.border, - border_variant: legacy_theme.lowest.variant.default.border, - border_focused: legacy_theme.lowest.accent.default.border, - border_transparent: transparent, - elevated_surface: legacy_theme.lowest.base.default.background, - surface: legacy_theme.middle.base.default.background, - background: legacy_theme.lowest.base.default.background, - filled_element: legacy_theme.lowest.base.default.background, - filled_element_hover: hsla(0.0, 0.0, 100.0, 0.12), - filled_element_active: hsla(0.0, 0.0, 100.0, 0.16), - filled_element_selected: legacy_theme.lowest.accent.default.background, - filled_element_disabled: transparent, - ghost_element: transparent, - ghost_element_hover: hsla(0.0, 0.0, 100.0, 0.08), - ghost_element_active: hsla(0.0, 0.0, 100.0, 0.12), - ghost_element_selected: legacy_theme.lowest.accent.default.background, - ghost_element_disabled: transparent, - text: legacy_theme.lowest.base.default.foreground, - text_muted: legacy_theme.lowest.variant.default.foreground, - /// TODO: map this to a real value - text_placeholder: legacy_theme.lowest.negative.default.foreground, - text_disabled: legacy_theme.lowest.base.disabled.foreground, - text_accent: legacy_theme.lowest.accent.default.foreground, - icon_muted: legacy_theme.lowest.variant.default.foreground, - syntax: SyntaxTheme { - highlights: json_theme - .editor - .syntax - .iter() - .map(|(token, style)| (token.clone(), style.color.clone().into())) - .collect(), - }, - status_bar: legacy_theme.lowest.base.default.background, - title_bar: legacy_theme.lowest.base.default.background, - toolbar: legacy_theme.highest.base.default.background, - tab_bar: legacy_theme.middle.base.default.background, - editor: legacy_theme.highest.base.default.background, - editor_subheader: legacy_theme.middle.base.default.background, - terminal: legacy_theme.highest.base.default.background, - editor_active_line: legacy_theme.highest.on.default.background, - image_fallback_background: legacy_theme.lowest.base.default.background, - - git_created: legacy_theme.lowest.positive.default.foreground, - git_modified: legacy_theme.lowest.accent.default.foreground, - git_deleted: legacy_theme.lowest.negative.default.foreground, - git_conflict: legacy_theme.lowest.warning.default.foreground, - git_ignored: legacy_theme.lowest.base.disabled.foreground, - git_renamed: legacy_theme.lowest.warning.default.foreground, - - players, - }; - - Ok(theme) -} - -#[derive(Deserialize)] -struct JsonTheme { - pub editor: JsonEditorTheme, - pub base_theme: serde_json::Value, -} - -#[derive(Deserialize)] -struct JsonEditorTheme { - pub syntax: HashMap, -} - -#[derive(Deserialize)] -struct JsonSyntaxStyle { - pub color: Hsla, -} - -/// Loads the [`Theme`] with the given 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")?; - - let legacy_theme: LegacyTheme = serde_json::from_value(json_theme.base_theme.clone()) - .context("failed to parse `base_theme`")?; - - Ok((json_theme, legacy_theme)) -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct LegacyTheme { - 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) -} diff --git a/crates/theme_converter/src/theme_printer.rs b/crates/theme_converter/src/theme_printer.rs deleted file mode 100644 index 3a9bdb159b..0000000000 --- a/crates/theme_converter/src/theme_printer.rs +++ /dev/null @@ -1,174 +0,0 @@ -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 272f85646050d997c755ca3885080ae6ff1a4195 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 04:33:51 +0100 Subject: [PATCH 12/18] Use `Refineable` for `ThemeStyles` (#3196) This PR updates the `ThemeStyles` struct to use the `Refineable` trait instead of a custom declarative macro for generating refinements. Release Notes: - N/A --- crates/theme2/src/colors.rs | 23 ++++++++-------- crates/theme2/src/default_theme.rs | 6 ++--- crates/theme2/src/syntax.rs | 2 +- crates/theme2/src/theme2.rs | 3 +-- crates/theme2/src/utils.rs | 43 ------------------------------ 5 files changed, 17 insertions(+), 60 deletions(-) delete mode 100644 crates/theme2/src/utils.rs diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index 2a59fa41bd..02c93a2e98 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -1,8 +1,9 @@ use gpui2::Hsla; use refineable::Refineable; -use crate::{generate_struct_with_overrides, SyntaxTheme}; +use crate::SyntaxTheme; +#[derive(Clone)] pub struct SystemColors { pub transparent: Hsla, pub mac_os_traffic_light_red: Hsla, @@ -17,6 +18,7 @@ pub struct PlayerColor { pub selection: Hsla, } +#[derive(Clone)] pub struct PlayerColors(pub Vec); #[derive(Refineable, Clone, Debug)] @@ -46,7 +48,7 @@ pub struct GitStatusColors { pub renamed: Hsla, } -#[derive(Refineable, Clone, Debug)] +#[derive(Refineable, Clone, Debug, Default)] #[refineable(debug)] pub struct ThemeColors { pub border: Hsla, @@ -86,15 +88,14 @@ pub struct ThemeColors { pub editor_active_line: Hsla, } -generate_struct_with_overrides! { - ThemeStyle, - ThemeStyleOverrides, - system: SystemColors, - colors: ThemeColors, - status: StatusColors, - git: GitStatusColors, - player: PlayerColors, - syntax: SyntaxTheme +#[derive(Refineable, Clone)] +pub struct ThemeStyles { + pub system: SystemColors, + pub colors: ThemeColors, + pub status: StatusColors, + pub git: GitStatusColors, + pub player: PlayerColors, + pub syntax: SyntaxTheme, } #[cfg(test)] diff --git a/crates/theme2/src/default_theme.rs b/crates/theme2/src/default_theme.rs index 26a55b5e0d..d7360b6f71 100644 --- a/crates/theme2/src/default_theme.rs +++ b/crates/theme2/src/default_theme.rs @@ -1,5 +1,5 @@ use crate::{ - colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyle}, + colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyles}, default_color_scales, Appearance, SyntaxTheme, ThemeFamily, ThemeVariant, }; @@ -8,7 +8,7 @@ fn zed_pro_daylight() -> ThemeVariant { id: "zed_pro_daylight".to_string(), name: "Zed Pro Daylight".into(), appearance: Appearance::Light, - styles: ThemeStyle { + styles: ThemeStyles { system: SystemColors::default(), colors: ThemeColors::default_light(), status: StatusColors::default(), @@ -24,7 +24,7 @@ pub(crate) fn zed_pro_moonlight() -> ThemeVariant { id: "zed_pro_moonlight".to_string(), name: "Zed Pro Moonlight".into(), appearance: Appearance::Dark, - styles: ThemeStyle { + styles: ThemeStyles { system: SystemColors::default(), colors: ThemeColors::default_dark(), status: StatusColors::default(), diff --git a/crates/theme2/src/syntax.rs b/crates/theme2/src/syntax.rs index 1cf8564bca..a8127f0c44 100644 --- a/crates/theme2/src/syntax.rs +++ b/crates/theme2/src/syntax.rs @@ -1,6 +1,6 @@ use gpui2::{HighlightStyle, Hsla}; -#[derive(Clone)] +#[derive(Clone, Default)] pub struct SyntaxTheme { pub highlights: Vec<(String, HighlightStyle)>, } diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 372e976bd3..34727eaf89 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -5,7 +5,6 @@ mod registry; mod scale; mod settings; mod syntax; -mod utils; pub use colors::*; pub use default_colors::*; @@ -55,7 +54,7 @@ pub struct ThemeVariant { pub(crate) id: String, pub name: SharedString, pub appearance: Appearance, - pub styles: ThemeStyle, + pub styles: ThemeStyles, } impl ThemeVariant { diff --git a/crates/theme2/src/utils.rs b/crates/theme2/src/utils.rs deleted file mode 100644 index ccdcde4274..0000000000 --- a/crates/theme2/src/utils.rs +++ /dev/null @@ -1,43 +0,0 @@ -/// This macro generates a struct and a corresponding struct with optional fields. -/// -/// It takes as input the name of the struct to be generated, the name of the struct with optional fields, -/// and a list of field names along with their types. -/// -/// # Example -/// ``` -/// generate_struct_with_overrides!( -/// MyStruct, -/// MyStructOverride, -/// field1: i32, -/// field2: String -/// ); -/// ``` -/// This will generate the following structs: -/// ``` -/// pub struct MyStruct { -/// pub field1: i32, -/// pub field2: String, -/// } -/// -/// pub struct MyStructOverride { -/// pub field1: Option, -/// pub field2: Option, -/// } -/// ``` -#[macro_export] -macro_rules! generate_struct_with_overrides { - ($struct_name:ident, $struct_override_name:ident, $($field:ident: $type:ty),*) => { - pub struct $struct_name { - $( - pub $field: $type, - )* - } - - #[allow(dead_code)] - pub struct $struct_override_name { - $( - pub $field: Option<$type>, - )* - } - }; -} From bbe53895efb25233a4ef42ea701054d1efdc0f59 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 15:45:42 +0100 Subject: [PATCH 13/18] Return `ColorScaleSet`s from individual color scale functions (#3197) This PR adjusts the individual color scale functions to return `ColorScaleSet`s instead of `DefaultColorScaleSet`s. We only use the `DefaultColorScaleSet`s to simplify the construction of the scales, so it isn't necessary to surface them outside of the function. Release Notes: - N/A --- crates/theme2/src/default_colors.rs | 165 +++++++++++++++++----------- 1 file changed, 99 insertions(+), 66 deletions(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 5ef93d036f..8fb38e9661 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -299,43 +299,43 @@ impl From for ColorScaleSet { pub fn default_color_scales() -> ColorScales { ColorScales { - 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(), + gray: gray(), + mauve: mauve(), + slate: slate(), + sage: sage(), + olive: olive(), + sand: sand(), + gold: gold(), + bronze: bronze(), + brown: brown(), + yellow: yellow(), + amber: amber(), + orange: orange(), + tomato: tomato(), + red: red(), + ruby: ruby(), + crimson: crimson(), + pink: pink(), + plum: plum(), + purple: purple(), + violet: violet(), + iris: iris(), + indigo: indigo(), + blue: blue(), + cyan: cyan(), + teal: teal(), + jade: jade(), + green: green(), + grass: grass(), + lime: lime(), + mint: mint(), + sky: sky(), + black: black(), + white: white(), } } -fn gray() -> DefaultColorScaleSet { +fn gray() -> ColorScaleSet { DefaultColorScaleSet { scale: "Gray", light: [ @@ -395,9 +395,10 @@ fn gray() -> DefaultColorScaleSet { "#ffffffed", ], } + .into() } -fn mauve() -> DefaultColorScaleSet { +fn mauve() -> ColorScaleSet { DefaultColorScaleSet { scale: "Mauve", light: [ @@ -457,9 +458,10 @@ fn mauve() -> DefaultColorScaleSet { "#fdfdffef", ], } + .into() } -fn slate() -> DefaultColorScaleSet { +fn slate() -> ColorScaleSet { DefaultColorScaleSet { scale: "Slate", light: [ @@ -519,9 +521,10 @@ fn slate() -> DefaultColorScaleSet { "#fcfdffef", ], } + .into() } -fn sage() -> DefaultColorScaleSet { +fn sage() -> ColorScaleSet { DefaultColorScaleSet { scale: "Sage", light: [ @@ -581,9 +584,10 @@ fn sage() -> DefaultColorScaleSet { "#fdfffeed", ], } + .into() } -fn olive() -> DefaultColorScaleSet { +fn olive() -> ColorScaleSet { DefaultColorScaleSet { scale: "Olive", light: [ @@ -643,9 +647,10 @@ fn olive() -> DefaultColorScaleSet { "#fdfffded", ], } + .into() } -fn sand() -> DefaultColorScaleSet { +fn sand() -> ColorScaleSet { DefaultColorScaleSet { scale: "Sand", light: [ @@ -705,9 +710,10 @@ fn sand() -> DefaultColorScaleSet { "#fffffded", ], } + .into() } -fn gold() -> DefaultColorScaleSet { +fn gold() -> ColorScaleSet { DefaultColorScaleSet { scale: "Gold", light: [ @@ -767,9 +773,10 @@ fn gold() -> DefaultColorScaleSet { "#fef7ede7", ], } + .into() } -fn bronze() -> DefaultColorScaleSet { +fn bronze() -> ColorScaleSet { DefaultColorScaleSet { scale: "Bronze", light: [ @@ -829,9 +836,10 @@ fn bronze() -> DefaultColorScaleSet { "#fff1e9ec", ], } + .into() } -fn brown() -> DefaultColorScaleSet { +fn brown() -> ColorScaleSet { DefaultColorScaleSet { scale: "Brown", light: [ @@ -891,9 +899,10 @@ fn brown() -> DefaultColorScaleSet { "#feecd4f2", ], } + .into() } -fn yellow() -> DefaultColorScaleSet { +fn yellow() -> ColorScaleSet { DefaultColorScaleSet { scale: "Yellow", light: [ @@ -953,9 +962,10 @@ fn yellow() -> DefaultColorScaleSet { "#fef6baf6", ], } + .into() } -fn amber() -> DefaultColorScaleSet { +fn amber() -> ColorScaleSet { DefaultColorScaleSet { scale: "Amber", light: [ @@ -1015,9 +1025,10 @@ fn amber() -> DefaultColorScaleSet { "#ffe7b3ff", ], } + .into() } -fn orange() -> DefaultColorScaleSet { +fn orange() -> ColorScaleSet { DefaultColorScaleSet { scale: "Orange", light: [ @@ -1077,9 +1088,10 @@ fn orange() -> DefaultColorScaleSet { "#ffe0c2ff", ], } + .into() } -fn tomato() -> DefaultColorScaleSet { +fn tomato() -> ColorScaleSet { DefaultColorScaleSet { scale: "Tomato", light: [ @@ -1139,9 +1151,10 @@ fn tomato() -> DefaultColorScaleSet { "#ffd6cefb", ], } + .into() } -fn red() -> DefaultColorScaleSet { +fn red() -> ColorScaleSet { DefaultColorScaleSet { scale: "Red", light: [ @@ -1201,9 +1214,10 @@ fn red() -> DefaultColorScaleSet { "#ffd1d9ff", ], } + .into() } -fn ruby() -> DefaultColorScaleSet { +fn ruby() -> ColorScaleSet { DefaultColorScaleSet { scale: "Ruby", light: [ @@ -1263,9 +1277,10 @@ fn ruby() -> DefaultColorScaleSet { "#ffd3e2fe", ], } + .into() } -fn crimson() -> DefaultColorScaleSet { +fn crimson() -> ColorScaleSet { DefaultColorScaleSet { scale: "Crimson", light: [ @@ -1325,9 +1340,10 @@ fn crimson() -> DefaultColorScaleSet { "#ffd5eafd", ], } + .into() } -fn pink() -> DefaultColorScaleSet { +fn pink() -> ColorScaleSet { DefaultColorScaleSet { scale: "Pink", light: [ @@ -1387,9 +1403,10 @@ fn pink() -> DefaultColorScaleSet { "#ffd3ecfd", ], } + .into() } -fn plum() -> DefaultColorScaleSet { +fn plum() -> ColorScaleSet { DefaultColorScaleSet { scale: "Plum", light: [ @@ -1449,9 +1466,10 @@ fn plum() -> DefaultColorScaleSet { "#feddfef4", ], } + .into() } -fn purple() -> DefaultColorScaleSet { +fn purple() -> ColorScaleSet { DefaultColorScaleSet { scale: "Purple", light: [ @@ -1511,9 +1529,10 @@ fn purple() -> DefaultColorScaleSet { "#f1ddfffa", ], } + .into() } -fn violet() -> DefaultColorScaleSet { +fn violet() -> ColorScaleSet { DefaultColorScaleSet { scale: "Violet", light: [ @@ -1573,9 +1592,10 @@ fn violet() -> DefaultColorScaleSet { "#e3defffe", ], } + .into() } -fn iris() -> DefaultColorScaleSet { +fn iris() -> ColorScaleSet { DefaultColorScaleSet { scale: "Iris", light: [ @@ -1635,9 +1655,10 @@ fn iris() -> DefaultColorScaleSet { "#e1e0fffe", ], } + .into() } -fn indigo() -> DefaultColorScaleSet { +fn indigo() -> ColorScaleSet { DefaultColorScaleSet { scale: "Indigo", light: [ @@ -1697,9 +1718,10 @@ fn indigo() -> DefaultColorScaleSet { "#d6e1ffff", ], } + .into() } -fn blue() -> DefaultColorScaleSet { +fn blue() -> ColorScaleSet { DefaultColorScaleSet { scale: "Blue", light: [ @@ -1759,9 +1781,10 @@ fn blue() -> DefaultColorScaleSet { "#c2e6ffff", ], } + .into() } -fn cyan() -> DefaultColorScaleSet { +fn cyan() -> ColorScaleSet { DefaultColorScaleSet { scale: "Cyan", light: [ @@ -1821,9 +1844,10 @@ fn cyan() -> DefaultColorScaleSet { "#bbf3fef7", ], } + .into() } -fn teal() -> DefaultColorScaleSet { +fn teal() -> ColorScaleSet { DefaultColorScaleSet { scale: "Teal", light: [ @@ -1883,9 +1907,10 @@ fn teal() -> DefaultColorScaleSet { "#b8ffebef", ], } + .into() } -fn jade() -> DefaultColorScaleSet { +fn jade() -> ColorScaleSet { DefaultColorScaleSet { scale: "Jade", light: [ @@ -1945,9 +1970,10 @@ fn jade() -> DefaultColorScaleSet { "#b8ffe1ef", ], } + .into() } -fn green() -> DefaultColorScaleSet { +fn green() -> ColorScaleSet { DefaultColorScaleSet { scale: "Green", light: [ @@ -2007,9 +2033,10 @@ fn green() -> DefaultColorScaleSet { "#bbffd7f0", ], } + .into() } -fn grass() -> DefaultColorScaleSet { +fn grass() -> ColorScaleSet { DefaultColorScaleSet { scale: "Grass", light: [ @@ -2069,9 +2096,10 @@ fn grass() -> DefaultColorScaleSet { "#ceffceef", ], } + .into() } -fn lime() -> DefaultColorScaleSet { +fn lime() -> ColorScaleSet { DefaultColorScaleSet { scale: "Lime", light: [ @@ -2131,9 +2159,10 @@ fn lime() -> DefaultColorScaleSet { "#e9febff7", ], } + .into() } -fn mint() -> DefaultColorScaleSet { +fn mint() -> ColorScaleSet { DefaultColorScaleSet { scale: "Mint", light: [ @@ -2193,9 +2222,10 @@ fn mint() -> DefaultColorScaleSet { "#cbfee9f5", ], } + .into() } -fn sky() -> DefaultColorScaleSet { +fn sky() -> ColorScaleSet { DefaultColorScaleSet { scale: "Sky", light: [ @@ -2255,9 +2285,10 @@ fn sky() -> DefaultColorScaleSet { "#c2f3ffff", ], } + .into() } -fn black() -> DefaultColorScaleSet { +fn black() -> ColorScaleSet { DefaultColorScaleSet { scale: "Black", light: [ @@ -2317,9 +2348,10 @@ fn black() -> DefaultColorScaleSet { "#000000f2", ], } + .into() } -fn white() -> DefaultColorScaleSet { +fn white() -> ColorScaleSet { DefaultColorScaleSet { scale: "White", light: [ @@ -2379,4 +2411,5 @@ fn white() -> DefaultColorScaleSet { "#fffffff2", ], } + .into() } From 51fa80ef066d28816f47d095e76e9c5ef0aede19 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 1 Nov 2023 09:19:32 -0700 Subject: [PATCH 14/18] ported example app, live_kit_client2 is done --- crates/live_kit_client2/examples/test_app.rs | 299 +++++++++--------- .../live_kit_client2/src/live_kit_client2.rs | 12 +- crates/live_kit_client2/src/test.rs | 4 +- 3 files changed, 159 insertions(+), 156 deletions(-) diff --git a/crates/live_kit_client2/examples/test_app.rs b/crates/live_kit_client2/examples/test_app.rs index 2147f6ab8c..ad10a4c95d 100644 --- a/crates/live_kit_client2/examples/test_app.rs +++ b/crates/live_kit_client2/examples/test_app.rs @@ -1,175 +1,178 @@ -// use std::time::Duration; -// todo!() +use std::{sync::Arc, time::Duration}; -// use futures::StreamExt; -// use gpui2::{actions, keymap_matcher::Binding, Menu, MenuItem}; -// use live_kit_client2::{ -// LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room, -// }; -// use live_kit_server::token::{self, VideoGrant}; -// use log::LevelFilter; -// use simplelog::SimpleLogger; +use futures::StreamExt; +use gpui2::KeyBinding; +use live_kit_client2::{ + LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room, +}; +use live_kit_server::token::{self, VideoGrant}; +use log::LevelFilter; +use serde_derive::Deserialize; +use simplelog::SimpleLogger; -// actions!(capture, [Quit]); +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)] +struct Quit; fn main() { - // SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); + SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - // gpui2::App::new(()).unwrap().run(|cx| { - // #[cfg(any(test, feature = "test-support"))] - // println!("USING TEST LIVEKIT"); + gpui2::App::production(Arc::new(())).run(|cx| { + #[cfg(any(test, feature = "test-support"))] + println!("USING TEST LIVEKIT"); - // #[cfg(not(any(test, feature = "test-support")))] - // println!("USING REAL LIVEKIT"); + #[cfg(not(any(test, feature = "test-support")))] + println!("USING REAL LIVEKIT"); - // cx.platform().activate(true); - // cx.add_global_action(quit); + cx.activate(true); - // cx.add_bindings([Binding::new("cmd-q", Quit, None)]); - // cx.set_menus(vec![Menu { - // name: "Zed", - // items: vec![MenuItem::Action { - // name: "Quit", - // action: Box::new(Quit), - // os_action: None, - // }], - // }]); + cx.on_action(quit); + cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - // let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap_or("http://localhost:7880".into()); - // let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap_or("devkey".into()); - // let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap_or("secret".into()); + // todo!() + // cx.set_menus(vec![Menu { + // name: "Zed", + // items: vec![MenuItem::Action { + // name: "Quit", + // action: Box::new(Quit), + // os_action: None, + // }], + // }]); - // cx.spawn(|cx| async move { - // let user_a_token = token::create( - // &live_kit_key, - // &live_kit_secret, - // Some("test-participant-1"), - // VideoGrant::to_join("test-room"), - // ) - // .unwrap(); - // let room_a = Room::new(); - // room_a.connect(&live_kit_url, &user_a_token).await.unwrap(); + let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap_or("http://localhost:7880".into()); + let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap_or("devkey".into()); + let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap_or("secret".into()); - // let user2_token = token::create( - // &live_kit_key, - // &live_kit_secret, - // Some("test-participant-2"), - // VideoGrant::to_join("test-room"), - // ) - // .unwrap(); - // let room_b = Room::new(); - // room_b.connect(&live_kit_url, &user2_token).await.unwrap(); + cx.spawn_on_main(|cx| async move { + let user_a_token = token::create( + &live_kit_key, + &live_kit_secret, + Some("test-participant-1"), + VideoGrant::to_join("test-room"), + ) + .unwrap(); + let room_a = Room::new(); + room_a.connect(&live_kit_url, &user_a_token).await.unwrap(); - // let mut audio_track_updates = room_b.remote_audio_track_updates(); - // let audio_track = LocalAudioTrack::create(); - // let audio_track_publication = room_a.publish_audio_track(audio_track).await.unwrap(); + let user2_token = token::create( + &live_kit_key, + &live_kit_secret, + Some("test-participant-2"), + VideoGrant::to_join("test-room"), + ) + .unwrap(); + let room_b = Room::new(); + room_b.connect(&live_kit_url, &user2_token).await.unwrap(); - // if let RemoteAudioTrackUpdate::Subscribed(track, _) = - // audio_track_updates.next().await.unwrap() - // { - // let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); - // assert_eq!(remote_tracks.len(), 1); - // assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1"); - // assert_eq!(track.publisher_id(), "test-participant-1"); - // } else { - // panic!("unexpected message"); - // } + let mut audio_track_updates = room_b.remote_audio_track_updates(); + let audio_track = LocalAudioTrack::create(); + let audio_track_publication = room_a.publish_audio_track(audio_track).await.unwrap(); - // audio_track_publication.set_mute(true).await.unwrap(); + if let RemoteAudioTrackUpdate::Subscribed(track, _) = + audio_track_updates.next().await.unwrap() + { + let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + assert_eq!(remote_tracks.len(), 1); + assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1"); + assert_eq!(track.publisher_id(), "test-participant-1"); + } else { + panic!("unexpected message"); + } - // println!("waiting for mute changed!"); - // if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = - // audio_track_updates.next().await.unwrap() - // { - // let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); - // assert_eq!(remote_tracks[0].sid(), track_id); - // assert_eq!(muted, true); - // } else { - // panic!("unexpected message"); - // } + audio_track_publication.set_mute(true).await.unwrap(); - // audio_track_publication.set_mute(false).await.unwrap(); + println!("waiting for mute changed!"); + if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = + audio_track_updates.next().await.unwrap() + { + let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + assert_eq!(remote_tracks[0].sid(), track_id); + assert_eq!(muted, true); + } else { + panic!("unexpected message"); + } - // if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = - // audio_track_updates.next().await.unwrap() - // { - // let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); - // assert_eq!(remote_tracks[0].sid(), track_id); - // assert_eq!(muted, false); - // } else { - // panic!("unexpected message"); - // } + audio_track_publication.set_mute(false).await.unwrap(); - // println!("Pausing for 5 seconds to test audio, make some noise!"); - // let timer = cx.background().timer(Duration::from_secs(5)); - // timer.await; - // let remote_audio_track = room_b - // .remote_audio_tracks("test-participant-1") - // .pop() - // .unwrap(); - // room_a.unpublish_track(audio_track_publication); + if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = + audio_track_updates.next().await.unwrap() + { + let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + assert_eq!(remote_tracks[0].sid(), track_id); + assert_eq!(muted, false); + } else { + panic!("unexpected message"); + } - // // Clear out any active speakers changed messages - // let mut next = audio_track_updates.next().await.unwrap(); - // while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } = next { - // println!("Speakers changed: {:?}", speakers); - // next = audio_track_updates.next().await.unwrap(); - // } + println!("Pausing for 5 seconds to test audio, make some noise!"); + let timer = cx.executor().timer(Duration::from_secs(5)); + timer.await; + let remote_audio_track = room_b + .remote_audio_tracks("test-participant-1") + .pop() + .unwrap(); + room_a.unpublish_track(audio_track_publication); - // if let RemoteAudioTrackUpdate::Unsubscribed { - // publisher_id, - // track_id, - // } = next - // { - // assert_eq!(publisher_id, "test-participant-1"); - // assert_eq!(remote_audio_track.sid(), track_id); - // assert_eq!(room_b.remote_audio_tracks("test-participant-1").len(), 0); - // } else { - // panic!("unexpected message"); - // } + // Clear out any active speakers changed messages + let mut next = audio_track_updates.next().await.unwrap(); + while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } = next { + println!("Speakers changed: {:?}", speakers); + next = audio_track_updates.next().await.unwrap(); + } - // let mut video_track_updates = room_b.remote_video_track_updates(); - // let displays = room_a.display_sources().await.unwrap(); - // let display = displays.into_iter().next().unwrap(); + if let RemoteAudioTrackUpdate::Unsubscribed { + publisher_id, + track_id, + } = next + { + assert_eq!(publisher_id, "test-participant-1"); + assert_eq!(remote_audio_track.sid(), track_id); + assert_eq!(room_b.remote_audio_tracks("test-participant-1").len(), 0); + } else { + panic!("unexpected message"); + } - // let local_video_track = LocalVideoTrack::screen_share_for_display(&display); - // let local_video_track_publication = - // room_a.publish_video_track(local_video_track).await.unwrap(); + let mut video_track_updates = room_b.remote_video_track_updates(); + let displays = room_a.display_sources().await.unwrap(); + let display = displays.into_iter().next().unwrap(); - // if let RemoteVideoTrackUpdate::Subscribed(track) = - // video_track_updates.next().await.unwrap() - // { - // let remote_video_tracks = room_b.remote_video_tracks("test-participant-1"); - // assert_eq!(remote_video_tracks.len(), 1); - // assert_eq!(remote_video_tracks[0].publisher_id(), "test-participant-1"); - // assert_eq!(track.publisher_id(), "test-participant-1"); - // } else { - // panic!("unexpected message"); - // } + let local_video_track = LocalVideoTrack::screen_share_for_display(&display); + let local_video_track_publication = + room_a.publish_video_track(local_video_track).await.unwrap(); - // let remote_video_track = room_b - // .remote_video_tracks("test-participant-1") - // .pop() - // .unwrap(); - // room_a.unpublish_track(local_video_track_publication); - // if let RemoteVideoTrackUpdate::Unsubscribed { - // publisher_id, - // track_id, - // } = video_track_updates.next().await.unwrap() - // { - // assert_eq!(publisher_id, "test-participant-1"); - // assert_eq!(remote_video_track.sid(), track_id); - // assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0); - // } else { - // panic!("unexpected message"); - // } + if let RemoteVideoTrackUpdate::Subscribed(track) = + video_track_updates.next().await.unwrap() + { + let remote_video_tracks = room_b.remote_video_tracks("test-participant-1"); + assert_eq!(remote_video_tracks.len(), 1); + assert_eq!(remote_video_tracks[0].publisher_id(), "test-participant-1"); + assert_eq!(track.publisher_id(), "test-participant-1"); + } else { + panic!("unexpected message"); + } - // cx.platform().quit(); - // }) - // .detach(); - // }); + let remote_video_track = room_b + .remote_video_tracks("test-participant-1") + .pop() + .unwrap(); + room_a.unpublish_track(local_video_track_publication); + if let RemoteVideoTrackUpdate::Unsubscribed { + publisher_id, + track_id, + } = video_track_updates.next().await.unwrap() + { + assert_eq!(publisher_id, "test-participant-1"); + assert_eq!(remote_video_track.sid(), track_id); + assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0); + } else { + panic!("unexpected message"); + } + + cx.update(|cx| cx.quit()).ok(); + }) + .detach(); + }); } -// fn quit(_: &Quit, cx: &mut gpui2::AppContext) { -// cx.platform().quit(); -// } +fn quit(_: &Quit, cx: &mut gpui2::AppContext) { + cx.quit(); +} diff --git a/crates/live_kit_client2/src/live_kit_client2.rs b/crates/live_kit_client2/src/live_kit_client2.rs index 35682382e9..47cc3873ff 100644 --- a/crates/live_kit_client2/src/live_kit_client2.rs +++ b/crates/live_kit_client2/src/live_kit_client2.rs @@ -1,11 +1,11 @@ -// #[cfg(not(any(test, feature = "test-support")))] +#[cfg(not(any(test, feature = "test-support")))] pub mod prod; -// #[cfg(not(any(test, feature = "test-support")))] +#[cfg(not(any(test, feature = "test-support")))] pub use prod::*; -// #[cfg(any(test, feature = "test-support"))] -// pub mod test; +#[cfg(any(test, feature = "test-support"))] +pub mod test; -// #[cfg(any(test, feature = "test-support"))] -// pub use test::*; +#[cfg(any(test, feature = "test-support"))] +pub use test::*; diff --git a/crates/live_kit_client2/src/test.rs b/crates/live_kit_client2/src/test.rs index 7185c11fa8..535ab20afb 100644 --- a/crates/live_kit_client2/src/test.rs +++ b/crates/live_kit_client2/src/test.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Result, Context}; use async_trait::async_trait; use collections::{BTreeMap, HashMap}; use futures::Stream; @@ -364,7 +364,7 @@ impl Room { let token = token.to_string(); async move { let server = TestServer::get(&url)?; - server.join_room(token.clone(), this.clone()).await?; + server.join_room(token.clone(), this.clone()).await.context("room join")?; *this.0.lock().connection.0.borrow_mut() = ConnectionState::Connected { url, token }; Ok(()) } From 1568ecbe1ee6c64a54597f3d02a31dc591f05624 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 1 Nov 2023 09:29:54 -0700 Subject: [PATCH 15/18] Add back room code to call2 --- crates/call2/src/participant.rs | 29 +- crates/call2/src/room.rs | 692 ++++++++++++++-------------- crates/live_kit_client2/src/prod.rs | 4 + crates/live_kit_client2/src/test.rs | 8 +- 4 files changed, 350 insertions(+), 383 deletions(-) diff --git a/crates/call2/src/participant.rs b/crates/call2/src/participant.rs index a1837e3ad0..9fe212e776 100644 --- a/crates/call2/src/participant.rs +++ b/crates/call2/src/participant.rs @@ -1,10 +1,12 @@ use anyhow::{anyhow, Result}; use client2::ParticipantIndex; use client2::{proto, User}; +use collections::HashMap; use gpui2::WeakModel; pub use live_kit_client2::Frame; +use live_kit_client2::{RemoteAudioTrack, RemoteVideoTrack}; use project2::Project; -use std::{fmt, sync::Arc}; +use std::sync::Arc; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ParticipantLocation { @@ -45,27 +47,6 @@ pub struct RemoteParticipant { pub participant_index: ParticipantIndex, pub muted: bool, pub speaking: bool, - // pub video_tracks: HashMap>, - // pub audio_tracks: HashMap>, -} - -#[derive(Clone)] -pub struct RemoteVideoTrack { - pub(crate) live_kit_track: Arc, -} - -unsafe impl Send for RemoteVideoTrack {} -// todo!("remove this sync because it's not legit") -unsafe impl Sync for RemoteVideoTrack {} - -impl fmt::Debug for RemoteVideoTrack { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RemoteVideoTrack").finish() - } -} - -impl RemoteVideoTrack { - pub fn frames(&self) -> async_broadcast::Receiver { - self.live_kit_track.frames() - } + pub video_tracks: HashMap>, + pub audio_tracks: HashMap>, } diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index f0e0b8de17..cf98db015b 100644 --- a/crates/call2/src/room.rs +++ b/crates/call2/src/room.rs @@ -1,9 +1,6 @@ -#![allow(dead_code, unused)] -// todo!() - use crate::{ call_settings::CallSettings, - participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack}, + participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}, IncomingCall, }; use anyhow::{anyhow, Result}; @@ -19,12 +16,15 @@ use gpui2::{ AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel, }; use language2::LanguageRegistry; -use live_kit_client2::{LocalTrackPublication, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate}; +use live_kit_client2::{ + LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteAudioTrackUpdate, + RemoteVideoTrackUpdate, +}; use postage::{sink::Sink, stream::Stream, watch}; use project2::Project; use settings2::Settings; -use std::{future::Future, sync::Arc, time::Duration}; -use util::{ResultExt, TryFutureExt}; +use std::{future::Future, mem, sync::Arc, time::Duration}; +use util::{post_inc, ResultExt, TryFutureExt}; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); @@ -95,15 +95,14 @@ impl Room { #[cfg(any(test, feature = "test-support"))] pub fn is_connected(&self) -> bool { - false - // if let Some(live_kit) = self.live_kit.as_ref() { - // matches!( - // *live_kit.room.status().borrow(), - // live_kit_client::ConnectionState::Connected { .. } - // ) - // } else { - // false - // } + if let Some(live_kit) = self.live_kit.as_ref() { + matches!( + *live_kit.room.status().borrow(), + live_kit_client2::ConnectionState::Connected { .. } + ) + } else { + false + } } fn new( @@ -423,7 +422,7 @@ impl Room { self.pending_participants.clear(); self.participant_user_ids.clear(); self.client_subscriptions.clear(); - // self.live_kit.take(); + self.live_kit.take(); self.pending_room_update.take(); self.maintain_connection.take(); } @@ -799,43 +798,43 @@ impl Room { location, muted: true, speaking: false, - // video_tracks: Default::default(), - // audio_tracks: Default::default(), + video_tracks: Default::default(), + audio_tracks: Default::default(), }, ); Audio::play_sound(Sound::Joined, cx); - // if let Some(live_kit) = this.live_kit.as_ref() { - // let video_tracks = - // live_kit.room.remote_video_tracks(&user.id.to_string()); - // let audio_tracks = - // live_kit.room.remote_audio_tracks(&user.id.to_string()); - // let publications = live_kit - // .room - // .remote_audio_track_publications(&user.id.to_string()); + if let Some(live_kit) = this.live_kit.as_ref() { + let video_tracks = + live_kit.room.remote_video_tracks(&user.id.to_string()); + let audio_tracks = + live_kit.room.remote_audio_tracks(&user.id.to_string()); + let publications = live_kit + .room + .remote_audio_track_publications(&user.id.to_string()); - // for track in video_tracks { - // this.remote_video_track_updated( - // RemoteVideoTrackUpdate::Subscribed(track), - // cx, - // ) - // .log_err(); - // } + for track in video_tracks { + this.remote_video_track_updated( + RemoteVideoTrackUpdate::Subscribed(track), + cx, + ) + .log_err(); + } - // for (track, publication) in - // audio_tracks.iter().zip(publications.iter()) - // { - // this.remote_audio_track_updated( - // RemoteAudioTrackUpdate::Subscribed( - // track.clone(), - // publication.clone(), - // ), - // cx, - // ) - // .log_err(); - // } - // } + for (track, publication) in + audio_tracks.iter().zip(publications.iter()) + { + this.remote_audio_track_updated( + RemoteAudioTrackUpdate::Subscribed( + track.clone(), + publication.clone(), + ), + cx, + ) + .log_err(); + } + } } } @@ -923,7 +922,6 @@ impl Room { change: RemoteVideoTrackUpdate, cx: &mut ModelContext, ) -> Result<()> { - todo!(); match change { RemoteVideoTrackUpdate::Subscribed(track) => { let user_id = track.publisher_id().parse()?; @@ -932,12 +930,7 @@ impl Room { .remote_participants .get_mut(&user_id) .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; - // participant.video_tracks.insert( - // track_id.clone(), - // Arc::new(RemoteVideoTrack { - // live_kit_track: track, - // }), - // ); + participant.video_tracks.insert(track_id.clone(), track); cx.emit(Event::RemoteVideoTracksChanged { participant_id: participant.peer_id, }); @@ -951,7 +944,7 @@ impl Room { .remote_participants .get_mut(&user_id) .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; - // participant.video_tracks.remove(&track_id); + participant.video_tracks.remove(&track_id); cx.emit(Event::RemoteVideoTracksChanged { participant_id: participant.peer_id, }); @@ -981,65 +974,61 @@ impl Room { participant.speaking = false; } } - // todo!() - // if let Some(id) = self.client.user_id() { - // if let Some(room) = &mut self.live_kit { - // if let Ok(_) = speaker_ids.binary_search(&id) { - // room.speaking = true; - // } else { - // room.speaking = false; - // } - // } - // } + if let Some(id) = self.client.user_id() { + if let Some(room) = &mut self.live_kit { + if let Ok(_) = speaker_ids.binary_search(&id) { + room.speaking = true; + } else { + room.speaking = false; + } + } + } cx.notify(); } RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => { - // todo!() - // let mut found = false; - // for participant in &mut self.remote_participants.values_mut() { - // for track in participant.audio_tracks.values() { - // if track.sid() == track_id { - // found = true; - // break; - // } - // } - // if found { - // participant.muted = muted; - // break; - // } - // } + let mut found = false; + for participant in &mut self.remote_participants.values_mut() { + for track in participant.audio_tracks.values() { + if track.sid() == track_id { + found = true; + break; + } + } + if found { + participant.muted = muted; + break; + } + } cx.notify(); } RemoteAudioTrackUpdate::Subscribed(track, publication) => { - // todo!() - // let user_id = track.publisher_id().parse()?; - // let track_id = track.sid().to_string(); - // let participant = self - // .remote_participants - // .get_mut(&user_id) - // .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; - // // participant.audio_tracks.insert(track_id.clone(), track); - // participant.muted = publication.is_muted(); + let user_id = track.publisher_id().parse()?; + let track_id = track.sid().to_string(); + let participant = self + .remote_participants + .get_mut(&user_id) + .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; + participant.audio_tracks.insert(track_id.clone(), track); + participant.muted = publication.is_muted(); - // cx.emit(Event::RemoteAudioTracksChanged { - // participant_id: participant.peer_id, - // }); + cx.emit(Event::RemoteAudioTracksChanged { + participant_id: participant.peer_id, + }); } RemoteAudioTrackUpdate::Unsubscribed { publisher_id, track_id, } => { - // todo!() - // let user_id = publisher_id.parse()?; - // let participant = self - // .remote_participants - // .get_mut(&user_id) - // .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; - // participant.audio_tracks.remove(&track_id); - // cx.emit(Event::RemoteAudioTracksChanged { - // participant_id: participant.peer_id, - // }); + let user_id = publisher_id.parse()?; + let participant = self + .remote_participants + .get_mut(&user_id) + .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; + participant.audio_tracks.remove(&track_id); + cx.emit(Event::RemoteAudioTracksChanged { + participant_id: participant.peer_id, + }); } } @@ -1220,278 +1209,269 @@ impl Room { } pub fn is_screen_sharing(&self) -> bool { - todo!() - // self.live_kit.as_ref().map_or(false, |live_kit| { - // !matches!(live_kit.screen_track, LocalTrack::None) - // }) + self.live_kit.as_ref().map_or(false, |live_kit| { + !matches!(live_kit.screen_track, LocalTrack::None) + }) } pub fn is_sharing_mic(&self) -> bool { - todo!() - // self.live_kit.as_ref().map_or(false, |live_kit| { - // !matches!(live_kit.microphone_track, LocalTrack::None) - // }) + self.live_kit.as_ref().map_or(false, |live_kit| { + !matches!(live_kit.microphone_track, LocalTrack::None) + }) } pub fn is_muted(&self, cx: &AppContext) -> bool { - todo!() - // self.live_kit - // .as_ref() - // .and_then(|live_kit| match &live_kit.microphone_track { - // LocalTrack::None => Some(Self::mute_on_join(cx)), - // LocalTrack::Pending { muted, .. } => Some(*muted), - // LocalTrack::Published { muted, .. } => Some(*muted), - // }) - // .unwrap_or(false) + self.live_kit + .as_ref() + .and_then(|live_kit| match &live_kit.microphone_track { + LocalTrack::None => Some(Self::mute_on_join(cx)), + LocalTrack::Pending { muted, .. } => Some(*muted), + LocalTrack::Published { muted, .. } => Some(*muted), + }) + .unwrap_or(false) } pub fn is_speaking(&self) -> bool { - todo!() - // self.live_kit - // .as_ref() - // .map_or(false, |live_kit| live_kit.speaking) + self.live_kit + .as_ref() + .map_or(false, |live_kit| live_kit.speaking) } pub fn is_deafened(&self) -> Option { - // self.live_kit.as_ref().map(|live_kit| live_kit.deafened) - todo!() + self.live_kit.as_ref().map(|live_kit| live_kit.deafened) } #[track_caller] pub fn share_microphone(&mut self, cx: &mut ModelContext) -> Task> { - todo!() - // if self.status.is_offline() { - // return Task::ready(Err(anyhow!("room is offline"))); - // } else if self.is_sharing_mic() { - // return Task::ready(Err(anyhow!("microphone was already shared"))); - // } + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } else if self.is_sharing_mic() { + return Task::ready(Err(anyhow!("microphone was already shared"))); + } - // let publish_id = if let Some(live_kit) = self.live_kit.as_mut() { - // let publish_id = post_inc(&mut live_kit.next_publish_id); - // live_kit.microphone_track = LocalTrack::Pending { - // publish_id, - // muted: false, - // }; - // cx.notify(); - // publish_id - // } else { - // return Task::ready(Err(anyhow!("live-kit was not initialized"))); - // }; + let publish_id = if let Some(live_kit) = self.live_kit.as_mut() { + let publish_id = post_inc(&mut live_kit.next_publish_id); + live_kit.microphone_track = LocalTrack::Pending { + publish_id, + muted: false, + }; + cx.notify(); + publish_id + } else { + return Task::ready(Err(anyhow!("live-kit was not initialized"))); + }; - // cx.spawn(move |this, mut cx| async move { - // let publish_track = async { - // let track = LocalAudioTrack::create(); - // this.upgrade() - // .ok_or_else(|| anyhow!("room was dropped"))? - // .update(&mut cx, |this, _| { - // this.live_kit - // .as_ref() - // .map(|live_kit| live_kit.room.publish_audio_track(track)) - // })? - // .ok_or_else(|| anyhow!("live-kit was not initialized"))? - // .await - // }; + cx.spawn(move |this, mut cx| async move { + let publish_track = async { + let track = LocalAudioTrack::create(); + this.upgrade() + .ok_or_else(|| anyhow!("room was dropped"))? + .update(&mut cx, |this, _| { + this.live_kit + .as_ref() + .map(|live_kit| live_kit.room.publish_audio_track(track)) + })? + .ok_or_else(|| anyhow!("live-kit was not initialized"))? + .await + }; - // let publication = publish_track.await; - // this.upgrade() - // .ok_or_else(|| anyhow!("room was dropped"))? - // .update(&mut cx, |this, cx| { - // let live_kit = this - // .live_kit - // .as_mut() - // .ok_or_else(|| anyhow!("live-kit was not initialized"))?; + let publication = publish_track.await; + this.upgrade() + .ok_or_else(|| anyhow!("room was dropped"))? + .update(&mut cx, |this, cx| { + let live_kit = this + .live_kit + .as_mut() + .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - // let (canceled, muted) = if let LocalTrack::Pending { - // publish_id: cur_publish_id, - // muted, - // } = &live_kit.microphone_track - // { - // (*cur_publish_id != publish_id, *muted) - // } else { - // (true, false) - // }; + let (canceled, muted) = if let LocalTrack::Pending { + publish_id: cur_publish_id, + muted, + } = &live_kit.microphone_track + { + (*cur_publish_id != publish_id, *muted) + } else { + (true, false) + }; - // match publication { - // Ok(publication) => { - // if canceled { - // live_kit.room.unpublish_track(publication); - // } else { - // if muted { - // cx.executor().spawn(publication.set_mute(muted)).detach(); - // } - // live_kit.microphone_track = LocalTrack::Published { - // track_publication: publication, - // muted, - // }; - // cx.notify(); - // } - // Ok(()) - // } - // Err(error) => { - // if canceled { - // Ok(()) - // } else { - // live_kit.microphone_track = LocalTrack::None; - // cx.notify(); - // Err(error) - // } - // } - // } - // })? - // }) + match publication { + Ok(publication) => { + if canceled { + live_kit.room.unpublish_track(publication); + } else { + if muted { + cx.executor().spawn(publication.set_mute(muted)).detach(); + } + live_kit.microphone_track = LocalTrack::Published { + track_publication: publication, + muted, + }; + cx.notify(); + } + Ok(()) + } + Err(error) => { + if canceled { + Ok(()) + } else { + live_kit.microphone_track = LocalTrack::None; + cx.notify(); + Err(error) + } + } + } + })? + }) } pub fn share_screen(&mut self, cx: &mut ModelContext) -> Task> { - todo!() - // if self.status.is_offline() { - // return Task::ready(Err(anyhow!("room is offline"))); - // } else if self.is_screen_sharing() { - // return Task::ready(Err(anyhow!("screen was already shared"))); - // } + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } else if self.is_screen_sharing() { + return Task::ready(Err(anyhow!("screen was already shared"))); + } - // let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { - // let publish_id = post_inc(&mut live_kit.next_publish_id); - // live_kit.screen_track = LocalTrack::Pending { - // publish_id, - // muted: false, - // }; - // cx.notify(); - // (live_kit.room.display_sources(), publish_id) - // } else { - // return Task::ready(Err(anyhow!("live-kit was not initialized"))); - // }; + let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { + let publish_id = post_inc(&mut live_kit.next_publish_id); + live_kit.screen_track = LocalTrack::Pending { + publish_id, + muted: false, + }; + cx.notify(); + (live_kit.room.display_sources(), publish_id) + } else { + return Task::ready(Err(anyhow!("live-kit was not initialized"))); + }; - // cx.spawn(move |this, mut cx| async move { - // let publish_track = async { - // let displays = displays.await?; - // let display = displays - // .first() - // .ok_or_else(|| anyhow!("no display found"))?; - // let track = LocalVideoTrack::screen_share_for_display(&display); - // this.upgrade() - // .ok_or_else(|| anyhow!("room was dropped"))? - // .update(&mut cx, |this, _| { - // this.live_kit - // .as_ref() - // .map(|live_kit| live_kit.room.publish_video_track(track)) - // })? - // .ok_or_else(|| anyhow!("live-kit was not initialized"))? - // .await - // }; + cx.spawn_on_main(move |this, mut cx| async move { + let publish_track = async { + let displays = displays.await?; + let display = displays + .first() + .ok_or_else(|| anyhow!("no display found"))?; + let track = LocalVideoTrack::screen_share_for_display(&display); + this.upgrade() + .ok_or_else(|| anyhow!("room was dropped"))? + .update(&mut cx, |this, _| { + this.live_kit + .as_ref() + .map(|live_kit| live_kit.room.publish_video_track(track)) + })? + .ok_or_else(|| anyhow!("live-kit was not initialized"))? + .await + }; - // let publication = publish_track.await; - // this.upgrade() - // .ok_or_else(|| anyhow!("room was dropped"))? - // .update(&mut cx, |this, cx| { - // let live_kit = this - // .live_kit - // .as_mut() - // .ok_or_else(|| anyhow!("live-kit was not initialized"))?; + let publication = publish_track.await; + this.upgrade() + .ok_or_else(|| anyhow!("room was dropped"))? + .update(&mut cx, |this, cx| { + let live_kit = this + .live_kit + .as_mut() + .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - // let (canceled, muted) = if let LocalTrack::Pending { - // publish_id: cur_publish_id, - // muted, - // } = &live_kit.screen_track - // { - // (*cur_publish_id != publish_id, *muted) - // } else { - // (true, false) - // }; + let (canceled, muted) = if let LocalTrack::Pending { + publish_id: cur_publish_id, + muted, + } = &live_kit.screen_track + { + (*cur_publish_id != publish_id, *muted) + } else { + (true, false) + }; - // match publication { - // Ok(publication) => { - // if canceled { - // live_kit.room.unpublish_track(publication); - // } else { - // if muted { - // cx.executor().spawn(publication.set_mute(muted)).detach(); - // } - // live_kit.screen_track = LocalTrack::Published { - // track_publication: publication, - // muted, - // }; - // cx.notify(); - // } + match publication { + Ok(publication) => { + if canceled { + live_kit.room.unpublish_track(publication); + } else { + if muted { + cx.executor().spawn(publication.set_mute(muted)).detach(); + } + live_kit.screen_track = LocalTrack::Published { + track_publication: publication, + muted, + }; + cx.notify(); + } - // Audio::play_sound(Sound::StartScreenshare, cx); + Audio::play_sound(Sound::StartScreenshare, cx); - // Ok(()) - // } - // Err(error) => { - // if canceled { - // Ok(()) - // } else { - // live_kit.screen_track = LocalTrack::None; - // cx.notify(); - // Err(error) - // } - // } - // } - // })? - // }) + Ok(()) + } + Err(error) => { + if canceled { + Ok(()) + } else { + live_kit.screen_track = LocalTrack::None; + cx.notify(); + Err(error) + } + } + } + })? + }) } pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> { - todo!() - // let should_mute = !self.is_muted(cx); - // if let Some(live_kit) = self.live_kit.as_mut() { - // if matches!(live_kit.microphone_track, LocalTrack::None) { - // return Ok(self.share_microphone(cx)); - // } + let should_mute = !self.is_muted(cx); + if let Some(live_kit) = self.live_kit.as_mut() { + if matches!(live_kit.microphone_track, LocalTrack::None) { + return Ok(self.share_microphone(cx)); + } - // let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?; - // live_kit.muted_by_user = should_mute; + let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?; + live_kit.muted_by_user = should_mute; - // if old_muted == true && live_kit.deafened == true { - // if let Some(task) = self.toggle_deafen(cx).ok() { - // task.detach(); - // } - // } + if old_muted == true && live_kit.deafened == true { + if let Some(task) = self.toggle_deafen(cx).ok() { + task.detach(); + } + } - // Ok(ret_task) - // } else { - // Err(anyhow!("LiveKit not started")) - // } + Ok(ret_task) + } else { + Err(anyhow!("LiveKit not started")) + } } pub fn toggle_deafen(&mut self, cx: &mut ModelContext) -> Result>> { - todo!() - // if let Some(live_kit) = self.live_kit.as_mut() { - // (*live_kit).deafened = !live_kit.deafened; + if let Some(live_kit) = self.live_kit.as_mut() { + (*live_kit).deafened = !live_kit.deafened; - // let mut tasks = Vec::with_capacity(self.remote_participants.len()); - // // Context notification is sent within set_mute itself. - // let mut mute_task = None; - // // When deafening, mute user's mic as well. - // // When undeafening, unmute user's mic unless it was manually muted prior to deafening. - // if live_kit.deafened || !live_kit.muted_by_user { - // mute_task = Some(live_kit.set_mute(live_kit.deafened, cx)?.0); - // }; - // for participant in self.remote_participants.values() { - // for track in live_kit - // .room - // .remote_audio_track_publications(&participant.user.id.to_string()) - // { - // let deafened = live_kit.deafened; - // tasks.push( - // cx.executor() - // .spawn_on_main(move || track.set_enabled(!deafened)), - // ); - // } - // } + let mut tasks = Vec::with_capacity(self.remote_participants.len()); + // Context notification is sent within set_mute itself. + let mut mute_task = None; + // When deafening, mute user's mic as well. + // When undeafening, unmute user's mic unless it was manually muted prior to deafening. + if live_kit.deafened || !live_kit.muted_by_user { + mute_task = Some(live_kit.set_mute(live_kit.deafened, cx)?.0); + }; + for participant in self.remote_participants.values() { + for track in live_kit + .room + .remote_audio_track_publications(&participant.user.id.to_string()) + { + let deafened = live_kit.deafened; + tasks.push( + cx.executor() + .spawn_on_main(move || track.set_enabled(!deafened)), + ); + } + } - // Ok(cx.executor().spawn_on_main(|| async { - // if let Some(mute_task) = mute_task { - // mute_task.await?; - // } - // for task in tasks { - // task.await?; - // } - // Ok(()) - // })) - // } else { - // Err(anyhow!("LiveKit not started")) - // } + Ok(cx.executor().spawn_on_main(|| async { + if let Some(mute_task) = mute_task { + mute_task.await?; + } + for task in tasks { + task.await?; + } + Ok(()) + })) + } else { + Err(anyhow!("LiveKit not started")) + } } pub fn unshare_screen(&mut self, cx: &mut ModelContext) -> Result<()> { @@ -1499,37 +1479,35 @@ impl Room { return Err(anyhow!("room is offline")); } - todo!() - // let live_kit = self - // .live_kit - // .as_mut() - // .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - // match mem::take(&mut live_kit.screen_track) { - // LocalTrack::None => Err(anyhow!("screen was not shared")), - // LocalTrack::Pending { .. } => { - // cx.notify(); - // Ok(()) - // } - // LocalTrack::Published { - // track_publication, .. - // } => { - // live_kit.room.unpublish_track(track_publication); - // cx.notify(); + let live_kit = self + .live_kit + .as_mut() + .ok_or_else(|| anyhow!("live-kit was not initialized"))?; + match mem::take(&mut live_kit.screen_track) { + LocalTrack::None => Err(anyhow!("screen was not shared")), + LocalTrack::Pending { .. } => { + cx.notify(); + Ok(()) + } + LocalTrack::Published { + track_publication, .. + } => { + live_kit.room.unpublish_track(track_publication); + cx.notify(); - // Audio::play_sound(Sound::StopScreenshare, cx); - // Ok(()) - // } - // } + Audio::play_sound(Sound::StopScreenshare, cx); + Ok(()) + } + } } #[cfg(any(test, feature = "test-support"))] pub fn set_display_sources(&self, sources: Vec) { - todo!() - // self.live_kit - // .as_ref() - // .unwrap() - // .room - // .set_display_sources(sources); + self.live_kit + .as_ref() + .unwrap() + .room + .set_display_sources(sources); } } diff --git a/crates/live_kit_client2/src/prod.rs b/crates/live_kit_client2/src/prod.rs index 65ed8b754f..b2b83e95fc 100644 --- a/crates/live_kit_client2/src/prod.rs +++ b/crates/live_kit_client2/src/prod.rs @@ -499,6 +499,10 @@ impl Room { rx, ) } + + pub fn set_display_sources(&self, _: Vec) { + unreachable!("This is a test-only function") + } } impl Drop for Room { diff --git a/crates/live_kit_client2/src/test.rs b/crates/live_kit_client2/src/test.rs index 535ab20afb..f1c3d39b8e 100644 --- a/crates/live_kit_client2/src/test.rs +++ b/crates/live_kit_client2/src/test.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result, Context}; +use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use collections::{BTreeMap, HashMap}; use futures::Stream; @@ -364,7 +364,10 @@ impl Room { let token = token.to_string(); async move { let server = TestServer::get(&url)?; - server.join_room(token.clone(), this.clone()).await.context("room join")?; + server + .join_room(token.clone(), this.clone()) + .await + .context("room join")?; *this.0.lock().connection.0.borrow_mut() = ConnectionState::Connected { url, token }; Ok(()) } @@ -547,6 +550,7 @@ impl LocalAudioTrack { } } +#[derive(Debug)] pub struct RemoteVideoTrack { sid: Sid, publisher_id: Sid, From 7d300d3f5b416532c9583f38d3160ec90e4a17e4 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 1 Nov 2023 12:34:26 -0400 Subject: [PATCH 16/18] v0.112.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca8767846f..3c6cc30645 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10821,7 +10821,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.111.0" +version = "0.112.0" dependencies = [ "activity_indicator", "ai", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 250a1814aa..c9012a3a14 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.111.0" +version = "0.112.0" publish = false [lib] From b910bbf0021002b9dee2a65e630194df73eae490 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 18:11:12 +0100 Subject: [PATCH 17/18] Add `ui_font_size` setting (#3199) This PR adds a new `ui_font_size` setting that can be used to control the scale of the entire UI. We use the value in this setting to set the base rem size of the window. Release Notes: - N/A --- Cargo.lock | 1 + crates/gpui2/src/window.rs | 6 ++++ crates/storybook2/src/storybook2.rs | 7 +++- crates/theme2/src/settings.rs | 12 +++++-- crates/ui2/Cargo.toml | 1 + crates/ui2/src/components/icon_button.rs | 6 ++-- crates/ui2/src/components/workspace.rs | 42 ++++++++++++------------ crates/ui2/src/elements/button.rs | 10 +++--- crates/ui2/src/elements/icon.rs | 6 ++-- crates/ui2/src/elements/label.rs | 4 +-- crates/ui2/src/prelude.rs | 11 +------ crates/ui2/src/settings.rs | 2 -- 12 files changed, 58 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea10948b5a..755f4440d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9666,6 +9666,7 @@ dependencies = [ "itertools 0.11.0", "rand 0.8.5", "serde", + "settings2", "smallvec", "strum", "theme2", diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 3997a3197f..7223826ab0 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -541,6 +541,12 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.rem_size } + /// Sets the size of an em for the base font of the application. Adjusting this value allows the + /// UI to scale, just like zooming a web page. + pub fn set_rem_size(&mut self, rem_size: impl Into) { + self.window.rem_size = rem_size.into(); + } + /// The line height associated with the current text style. pub fn line_height(&self) -> Pixels { let rem_size = self.rem_size(); diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 411fe18071..3b5722732b 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -81,7 +81,12 @@ fn main() { }), ..Default::default() }, - move |cx| cx.build_view(|cx| StoryWrapper::new(selector.story(cx))), + move |cx| { + let theme_settings = ThemeSettings::get_global(cx); + cx.set_rem_size(theme_settings.ui_font_size); + + cx.build_view(|cx| StoryWrapper::new(selector.story(cx))) + }, ); cx.activate(true); diff --git a/crates/theme2/src/settings.rs b/crates/theme2/src/settings.rs index bad00ee196..c8d2b52273 100644 --- a/crates/theme2/src/settings.rs +++ b/crates/theme2/src/settings.rs @@ -17,6 +17,7 @@ const MIN_LINE_HEIGHT: f32 = 1.0; #[derive(Clone)] pub struct ThemeSettings { + pub ui_font_size: Pixels, pub buffer_font: Font, pub buffer_font_size: Pixels, pub buffer_line_height: BufferLineHeight, @@ -28,6 +29,8 @@ pub struct AdjustedBufferFontSize(Option); #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct ThemeSettingsContent { + #[serde(default)] + pub ui_font_size: Option, #[serde(default)] pub buffer_font_family: Option, #[serde(default)] @@ -115,6 +118,7 @@ impl settings2::Settings for ThemeSettings { let themes = cx.default_global::>(); let mut this = Self { + ui_font_size: defaults.ui_font_size.unwrap_or(16.).into(), buffer_font: Font { family: defaults.buffer_font_family.clone().unwrap().into(), features: defaults.buffer_font_features.clone().unwrap(), @@ -123,9 +127,10 @@ impl settings2::Settings for ThemeSettings { }, buffer_font_size: defaults.buffer_font_size.unwrap().into(), buffer_line_height: defaults.buffer_line_height.unwrap(), - active_theme: themes.get("Zed Pro Moonlight").unwrap(), - // todo!(Read the theme name from the settings) - // active_theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), + active_theme: themes + .get(defaults.theme.as_ref().unwrap()) + .or(themes.get("Zed Pro Moonlight")) + .unwrap(), }; for value in user_values.into_iter().copied().cloned() { @@ -142,6 +147,7 @@ impl settings2::Settings for ThemeSettings { } } + merge(&mut this.ui_font_size, value.ui_font_size.map(Into::into)); merge( &mut this.buffer_font_size, value.buffer_font_size.map(Into::into), diff --git a/crates/ui2/Cargo.toml b/crates/ui2/Cargo.toml index 58013e34cd..f11fd652b6 100644 --- a/crates/ui2/Cargo.toml +++ b/crates/ui2/Cargo.toml @@ -10,6 +10,7 @@ chrono = "0.4" gpui2 = { path = "../gpui2" } itertools = { version = "0.11.0", optional = true } serde.workspace = true +settings2 = { path = "../settings2" } smallvec.workspace = true strum = { version = "0.25.0", features = ["derive"] } theme2 = { path = "../theme2" } diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index 06e242b1ef..101c845a76 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gpui2::MouseButton; +use gpui2::{rems, MouseButton}; use crate::{h_stack, prelude::*}; use crate::{ClickHandler, Icon, IconColor, IconElement}; @@ -88,8 +88,8 @@ impl IconButton { .id(self.id.clone()) .justify_center() .rounded_md() - .py(ui_size(cx, 0.25)) - .px(ui_size(cx, 6. / 14.)) + .py(rems(0.21875)) + .px(rems(0.375)) .bg(bg_color) .hover(|style| style.bg(bg_hover_color)) .active(|style| style.bg(bg_active_color)) diff --git a/crates/ui2/src/components/workspace.rs b/crates/ui2/src/components/workspace.rs index 0e31c6b9ad..97570a33e3 100644 --- a/crates/ui2/src/components/workspace.rs +++ b/crates/ui2/src/components/workspace.rs @@ -1,14 +1,16 @@ use std::sync::Arc; use chrono::DateTime; -use gpui2::{px, relative, rems, Div, Render, Size, View, VisualContext}; +use gpui2::{px, relative, Div, Render, Size, View, VisualContext}; +use settings2::Settings; +use theme2::ThemeSettings; -use crate::{prelude::*, NotificationsPanel}; +use crate::prelude::*; 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, + static_livestream, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, CollabPanel, + EditorPane, Label, LanguageSelector, NotificationsPanel, Pane, PaneGroup, Panel, + PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar, + Toast, ToastOrigin, }; #[derive(Clone)] @@ -150,6 +152,18 @@ impl Workspace { pub fn debug_toggle_user_settings(&mut self, cx: &mut ViewContext) { self.debug.enable_user_settings = !self.debug.enable_user_settings; + let mut theme_settings = ThemeSettings::get_global(cx).clone(); + + if self.debug.enable_user_settings { + theme_settings.ui_font_size = 18.0.into(); + } else { + theme_settings.ui_font_size = 16.0.into(); + } + + ThemeSettings::override_global(theme_settings.clone(), cx); + + cx.set_rem_size(theme_settings.ui_font_size); + cx.notify(); } @@ -179,20 +193,6 @@ impl Render for Workspace { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Div { - // HACK: This should happen inside of `debug_toggle_user_settings`, but - // we don't have `cx.global::()` in event handlers at the moment. - // Need to talk with Nathan/Antonio about this. - { - let settings = user_settings_mut(cx); - - if self.debug.enable_user_settings { - settings.list_indent_depth = SettingValue::UserDefined(rems(0.5).into()); - settings.ui_scale = SettingValue::UserDefined(1.25); - } else { - *settings = FakeSettings::default(); - } - } - let root_group = PaneGroup::new_panes( vec![Pane::new( "pane-0", @@ -321,7 +321,7 @@ impl Render for Workspace { v_stack() .z_index(9) .absolute() - .bottom_10() + .top_20() .left_1_4() .w_40() .gap_2() diff --git a/crates/ui2/src/elements/button.rs b/crates/ui2/src/elements/button.rs index e63269197c..073bcdbb45 100644 --- a/crates/ui2/src/elements/button.rs +++ b/crates/ui2/src/elements/button.rs @@ -1,9 +1,9 @@ use std::sync::Arc; -use gpui2::{div, DefiniteLength, Hsla, MouseButton, WindowContext}; +use gpui2::{div, rems, DefiniteLength, Hsla, MouseButton, WindowContext}; -use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor}; -use crate::{prelude::*, LineHeightStyle}; +use crate::prelude::*; +use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor, LineHeightStyle}; #[derive(Default, PartialEq, Clone, Copy)] pub enum IconPosition { @@ -151,7 +151,7 @@ impl Button { .relative() .id(SharedString::from(format!("{}", self.label))) .p_1() - .text_size(ui_size(cx, 1.)) + .text_size(rems(1.)) .rounded_md() .bg(self.variant.bg_color(cx)) .hover(|style| style.bg(self.variant.bg_color_hover(cx))) @@ -198,7 +198,7 @@ impl ButtonGroup { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let mut el = h_stack().text_size(ui_size(cx, 1.)); + let mut el = h_stack().text_size(rems(1.)); for button in self.buttons { el = el.child(button.render(_view, cx)); diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index 6c1b3a4f08..8cc62f4a8d 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -1,4 +1,4 @@ -use gpui2::{svg, Hsla}; +use gpui2::{rems, svg, Hsla}; use strum::EnumIter; use crate::prelude::*; @@ -175,8 +175,8 @@ impl IconElement { fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { let fill = self.color.color(cx); let svg_size = match self.size { - IconSize::Small => ui_size(cx, 12. / 14.), - IconSize::Medium => ui_size(cx, 15. / 14.), + IconSize::Small => rems(0.75), + IconSize::Medium => rems(0.9375), }; svg() diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index ee8ac9a636..dcc28a3319 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -1,4 +1,4 @@ -use gpui2::{relative, Hsla, WindowContext}; +use gpui2::{relative, rems, Hsla, WindowContext}; use smallvec::SmallVec; use crate::prelude::*; @@ -86,7 +86,7 @@ impl Label { .bg(LabelColor::Hidden.hsla(cx)), ) }) - .text_size(ui_size(cx, 1.)) + .text_size(rems(1.)) .when(self.line_height_style == LineHeightStyle::UILabel, |this| { this.line_height(relative(1.)) }) diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index b424ce6123..c3f530d70c 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -4,21 +4,12 @@ pub use gpui2::{ }; pub use crate::elevation::*; -use crate::settings::user_settings; pub use crate::ButtonVariant; pub use theme2::ActiveTheme; -use gpui2::{rems, Hsla, Rems}; +use gpui2::Hsla; use strum::EnumIter; -pub fn ui_size(cx: &mut WindowContext, size: f32) -> Rems { - const UI_SCALE_RATIO: f32 = 0.875; - - let settings = user_settings(cx); - - rems(*settings.ui_scale * UI_SCALE_RATIO * size) -} - #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum FileSystemStatus { #[default] diff --git a/crates/ui2/src/settings.rs b/crates/ui2/src/settings.rs index 48a2e8e7b4..6a9426f623 100644 --- a/crates/ui2/src/settings.rs +++ b/crates/ui2/src/settings.rs @@ -58,7 +58,6 @@ pub struct FakeSettings { pub list_disclosure_style: SettingValue, pub list_indent_depth: SettingValue, pub titlebar: TitlebarSettings, - pub ui_scale: SettingValue, } impl Default for FakeSettings { @@ -68,7 +67,6 @@ impl Default for FakeSettings { list_disclosure_style: SettingValue::Default(DisclosureControlStyle::ChevronOnHover), list_indent_depth: SettingValue::Default(rems(0.3).into()), default_panel_size: SettingValue::Default(rems(16.).into()), - ui_scale: SettingValue::Default(1.), } } } From 6aaeb23157dff73dcb4094d416dfd1bdc1d1aded Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 1 Nov 2023 10:52:15 -0700 Subject: [PATCH 18/18] Rename live kit bridge to 2 --- .../{LiveKitBridge => LiveKitBridge2}/Package.resolved | 0 .../{LiveKitBridge => LiveKitBridge2}/Package.swift | 8 ++++---- .../{LiveKitBridge => LiveKitBridge2}/README.md | 2 +- .../Sources/LiveKitBridge2/LiveKitBridge2.swift} | 0 crates/live_kit_client2/build.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename crates/live_kit_client2/{LiveKitBridge => LiveKitBridge2}/Package.resolved (100%) rename crates/live_kit_client2/{LiveKitBridge => LiveKitBridge2}/Package.swift (84%) rename crates/live_kit_client2/{LiveKitBridge => LiveKitBridge2}/README.md (65%) rename crates/live_kit_client2/{LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift => LiveKitBridge2/Sources/LiveKitBridge2/LiveKitBridge2.swift} (100%) diff --git a/crates/live_kit_client2/LiveKitBridge/Package.resolved b/crates/live_kit_client2/LiveKitBridge2/Package.resolved similarity index 100% rename from crates/live_kit_client2/LiveKitBridge/Package.resolved rename to crates/live_kit_client2/LiveKitBridge2/Package.resolved diff --git a/crates/live_kit_client2/LiveKitBridge/Package.swift b/crates/live_kit_client2/LiveKitBridge2/Package.swift similarity index 84% rename from crates/live_kit_client2/LiveKitBridge/Package.swift rename to crates/live_kit_client2/LiveKitBridge2/Package.swift index d7b5c271b9..890eaa2f6d 100644 --- a/crates/live_kit_client2/LiveKitBridge/Package.swift +++ b/crates/live_kit_client2/LiveKitBridge2/Package.swift @@ -3,16 +3,16 @@ import PackageDescription let package = Package( - name: "LiveKitBridge", + name: "LiveKitBridge2", platforms: [ .macOS(.v10_15) ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( - name: "LiveKitBridge", + name: "LiveKitBridge2", type: .static, - targets: ["LiveKitBridge"]), + targets: ["LiveKitBridge2"]), ], dependencies: [ .package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.0.12")), @@ -21,7 +21,7 @@ let package = Package( // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( - name: "LiveKitBridge", + name: "LiveKitBridge2", dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")]), ] ) diff --git a/crates/live_kit_client2/LiveKitBridge/README.md b/crates/live_kit_client2/LiveKitBridge2/README.md similarity index 65% rename from crates/live_kit_client2/LiveKitBridge/README.md rename to crates/live_kit_client2/LiveKitBridge2/README.md index b982c67286..1fceed8165 100644 --- a/crates/live_kit_client2/LiveKitBridge/README.md +++ b/crates/live_kit_client2/LiveKitBridge2/README.md @@ -1,3 +1,3 @@ -# LiveKitBridge +# LiveKitBridge2 A description of this package. diff --git a/crates/live_kit_client2/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client2/LiveKitBridge2/Sources/LiveKitBridge2/LiveKitBridge2.swift similarity index 100% rename from crates/live_kit_client2/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift rename to crates/live_kit_client2/LiveKitBridge2/Sources/LiveKitBridge2/LiveKitBridge2.swift diff --git a/crates/live_kit_client2/build.rs b/crates/live_kit_client2/build.rs index 1445704b46..b346b3168b 100644 --- a/crates/live_kit_client2/build.rs +++ b/crates/live_kit_client2/build.rs @@ -5,7 +5,7 @@ use std::{ process::Command, }; -const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge"; +const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge2"; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")]