diff --git a/zed/src/channel.rs b/zed/src/channel.rs index 0c3cb8bd2496dbdff7c618871237f72ff0027dd7..11991b371dbbf16353682e7e7bf5ac729279c1af 100644 --- a/zed/src/channel.rs +++ b/zed/src/channel.rs @@ -1,7 +1,7 @@ use crate::{ rpc::{self, Client}, user::{User, UserStore}, - util::TryFutureExt, + util::{post_inc, TryFutureExt}, }; use anyhow::{anyhow, Context, Result}; use gpui::{ @@ -39,8 +39,7 @@ pub struct Channel { details: ChannelDetails, messages: SumTree, loaded_all_messages: bool, - pending_messages: Vec, - next_local_message_id: u64, + next_pending_message_id: usize, user_store: Arc, rpc: Arc, _subscription: rpc::Subscription, @@ -48,20 +47,21 @@ pub struct Channel { #[derive(Clone, Debug)] pub struct ChannelMessage { - pub id: u64, + pub id: ChannelMessageId, pub body: String, pub timestamp: OffsetDateTime, pub sender: Arc, } -pub struct PendingChannelMessage { - pub body: String, - local_id: u64, +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum ChannelMessageId { + Saved(u64), + Pending(usize), } #[derive(Clone, Debug, Default)] pub struct ChannelMessageSummary { - max_id: u64, + max_id: ChannelMessageId, count: usize, } @@ -216,9 +216,8 @@ impl Channel { user_store, rpc, messages: Default::default(), - pending_messages: Default::default(), loaded_all_messages: false, - next_local_message_id: 0, + next_pending_message_id: 0, _subscription, } } @@ -236,13 +235,27 @@ impl Channel { Err(anyhow!("message body can't be empty"))?; } + let current_user = self + .user_store + .current_user() + .borrow() + .clone() + .ok_or_else(|| anyhow!("current_user is not present"))?; + let channel_id = self.details.id; - let local_id = self.next_local_message_id; - self.next_local_message_id += 1; - self.pending_messages.push(PendingChannelMessage { - local_id, - body: body.clone(), - }); + let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id)); + self.insert_messages( + SumTree::from_item( + ChannelMessage { + id: pending_id, + body: body.clone(), + sender: current_user, + timestamp: OffsetDateTime::now_utc(), + }, + &(), + ), + cx, + ); let user_store = self.user_store.clone(); let rpc = self.rpc.clone(); Ok(cx.spawn(|this, mut cx| async move { @@ -254,13 +267,8 @@ impl Channel { ) .await?; this.update(&mut cx, |this, cx| { - if let Ok(i) = this - .pending_messages - .binary_search_by_key(&local_id, |msg| msg.local_id) - { - this.pending_messages.remove(i); - this.insert_messages(SumTree::from_item(message, &()), cx); - } + this.remove_message(pending_id, cx); + this.insert_messages(SumTree::from_item(message, &()), cx); Ok(()) }) })) @@ -271,7 +279,12 @@ impl Channel { let rpc = self.rpc.clone(); let user_store = self.user_store.clone(); let channel_id = self.details.id; - if let Some(before_message_id) = self.messages.first().map(|message| message.id) { + if let Some(before_message_id) = + self.messages.first().and_then(|message| match message.id { + ChannelMessageId::Saved(id) => Some(id), + ChannelMessageId::Pending(_) => None, + }) + { cx.spawn(|this, mut cx| { async move { let response = rpc @@ -354,10 +367,6 @@ impl Channel { cursor.take(range.len()) } - pub fn pending_messages(&self) -> &[PendingChannelMessage] { - &self.pending_messages - } - fn handle_message_sent( &mut self, message: TypedEnvelope, @@ -384,9 +393,30 @@ impl Channel { Ok(()) } + fn remove_message(&mut self, message_id: ChannelMessageId, cx: &mut ModelContext) { + let mut old_cursor = self.messages.cursor::(); + let mut new_messages = old_cursor.slice(&message_id, Bias::Left, &()); + let start_ix = old_cursor.sum_start().0; + let removed_messages = old_cursor.slice(&message_id, Bias::Right, &()); + let removed_count = removed_messages.summary().count; + new_messages.push_tree(old_cursor.suffix(&()), &()); + + drop(old_cursor); + self.messages = new_messages; + + if removed_count > 0 { + let end_ix = start_ix + removed_count; + cx.emit(ChannelEvent::MessagesUpdated { + old_range: start_ix..end_ix, + new_count: 0, + }); + cx.notify(); + } + } + fn insert_messages(&mut self, messages: SumTree, cx: &mut ModelContext) { if let Some((first_message, last_message)) = messages.first().zip(messages.last()) { - let mut old_cursor = self.messages.cursor::(); + let mut old_cursor = self.messages.cursor::(); let mut new_messages = old_cursor.slice(&first_message.id, Bias::Left, &()); let start_ix = old_cursor.sum_start().0; let removed_messages = old_cursor.slice(&last_message.id, Bias::Right, &()); @@ -445,12 +475,16 @@ impl ChannelMessage { ) -> Result { let sender = user_store.fetch_user(message.sender_id).await?; Ok(ChannelMessage { - id: message.id, + id: ChannelMessageId::Saved(message.id), body: message.body, timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)?, sender, }) } + + pub fn is_pending(&self) -> bool { + matches!(self.id, ChannelMessageId::Pending(_)) + } } impl sum_tree::Item for ChannelMessage { @@ -464,6 +498,12 @@ impl sum_tree::Item for ChannelMessage { } } +impl Default for ChannelMessageId { + fn default() -> Self { + Self::Saved(0) + } +} + impl sum_tree::Summary for ChannelMessageSummary { type Context = (); @@ -473,7 +513,7 @@ impl sum_tree::Summary for ChannelMessageSummary { } } -impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for u64 { +impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for ChannelMessageId { fn add_summary(&mut self, summary: &'a ChannelMessageSummary, _: &()) { debug_assert!(summary.max_id > *self); *self = summary.max_id; diff --git a/zed/src/chat_panel.rs b/zed/src/chat_panel.rs index 7cc152116e00361f9dcfee2e7c1fdbff9b8dcb06..d7752b9a53e944b6e5c1776ab280fd3ea95f025b 100644 --- a/zed/src/chat_panel.rs +++ b/zed/src/chat_panel.rs @@ -230,7 +230,12 @@ impl ChatPanel { fn render_message(&self, message: &ChannelMessage) -> ElementBox { let now = OffsetDateTime::now_utc(); let settings = self.settings.borrow(); - let theme = &settings.theme.chat_panel.message; + let theme = if message.is_pending() { + &settings.theme.chat_panel.pending_message + } else { + &settings.theme.chat_panel.message + }; + Container::new( Flex::column() .with_child( diff --git a/zed/src/theme.rs b/zed/src/theme.rs index 88f385a05461b18a0db3f6182e9b3d08effb97b2..ef0798ab24ff843dabd5502fbb201cde36b0ef0b 100644 --- a/zed/src/theme.rs +++ b/zed/src/theme.rs @@ -95,6 +95,7 @@ pub struct ChatPanel { #[serde(flatten)] pub container: ContainerStyle, pub message: ChatMessage, + pub pending_message: ChatMessage, pub channel_select: ChannelSelect, pub input_editor: InputEditorStyle, pub sign_in_prompt: TextStyle, diff --git a/zed/src/theme/theme_registry.rs b/zed/src/theme/theme_registry.rs index cd9781afe942f4e5bee4d1c16b6aa5886ae376a0..18bcc9ebf2e35369200a6ea1dff009769c9da352 100644 --- a/zed/src/theme/theme_registry.rs +++ b/zed/src/theme/theme_registry.rs @@ -327,6 +327,24 @@ impl KeyPathReferenceSet { ); } } + + // If an existing reference's target path is a prefix of the new reference's target path, + // then insert this new reference before that existing reference. + for prefix in new_reference.target.prefixes() { + for id in Self::reference_ids_for_key_path( + prefix, + &self.references, + &self.reference_ids_by_target, + KeyPathReference::target, + PartialEq::eq, + ) { + Self::add_dependency( + (new_id, id), + &mut self.dependencies, + &mut self.dependency_counts, + ); + } + } } // Find all existing references that satisfy a given predicate with respect @@ -619,6 +637,38 @@ mod tests { ); } + #[gpui::test] + fn test_nested_extension(cx: &mut MutableAppContext) { + let assets = TestAssets(&[( + "themes/theme.toml", + r##" + [a] + text = { extends = "$text.0" } + + [b] + extends = "$a" + text = { extends = "$text.1" } + + [text] + 0 = { color = "red" } + 1 = { color = "blue" } + "##, + )]); + + let registry = ThemeRegistry::new(assets, cx.font_cache().clone()); + let theme_data = registry.load("theme", true).unwrap(); + assert_eq!( + theme_data + .get("b") + .unwrap() + .get("text") + .unwrap() + .get("color") + .unwrap(), + "blue" + ); + } + #[test] fn test_key_path_reference_set_simple() { let input_references = build_refs(&[