Get more of chat panel compiling, but lots of todos
This commit is contained in:
parent
6955579f19
commit
213ed2028c
12 changed files with 1058 additions and 992 deletions
|
@ -8,7 +8,8 @@ use collections::{hash_map, HashMap, HashSet};
|
||||||
use db::RELEASE_CHANNEL;
|
use db::RELEASE_CHANNEL;
|
||||||
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
|
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
|
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
|
||||||
|
WeakModel,
|
||||||
};
|
};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
proto::{self, ChannelVisibility},
|
proto::{self, ChannelVisibility},
|
||||||
|
@ -46,7 +47,7 @@ pub struct ChannelStore {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub id: ChannelId,
|
pub id: ChannelId,
|
||||||
pub name: String,
|
pub name: SharedString,
|
||||||
pub visibility: proto::ChannelVisibility,
|
pub visibility: proto::ChannelVisibility,
|
||||||
pub role: proto::ChannelRole,
|
pub role: proto::ChannelRole,
|
||||||
pub unseen_note_version: Option<(u64, clock::Global)>,
|
pub unseen_note_version: Option<(u64, clock::Global)>,
|
||||||
|
@ -895,14 +896,16 @@ impl ChannelStore {
|
||||||
.channel_invitations
|
.channel_invitations
|
||||||
.binary_search_by_key(&channel.id, |c| c.id)
|
.binary_search_by_key(&channel.id, |c| c.id)
|
||||||
{
|
{
|
||||||
Ok(ix) => Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name,
|
Ok(ix) => {
|
||||||
|
Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name.into()
|
||||||
|
}
|
||||||
Err(ix) => self.channel_invitations.insert(
|
Err(ix) => self.channel_invitations.insert(
|
||||||
ix,
|
ix,
|
||||||
Arc::new(Channel {
|
Arc::new(Channel {
|
||||||
id: channel.id,
|
id: channel.id,
|
||||||
visibility: channel.visibility(),
|
visibility: channel.visibility(),
|
||||||
role: channel.role(),
|
role: channel.role(),
|
||||||
name: channel.name,
|
name: channel.name.into(),
|
||||||
unseen_note_version: None,
|
unseen_note_version: None,
|
||||||
unseen_message_id: None,
|
unseen_message_id: None,
|
||||||
parent_path: channel.parent_path,
|
parent_path: channel.parent_path,
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
||||||
|
|
||||||
existing_channel.visibility = channel_proto.visibility();
|
existing_channel.visibility = channel_proto.visibility();
|
||||||
existing_channel.role = channel_proto.role();
|
existing_channel.role = channel_proto.role();
|
||||||
existing_channel.name = channel_proto.name;
|
existing_channel.name = channel_proto.name.into();
|
||||||
} else {
|
} else {
|
||||||
self.channels_by_id.insert(
|
self.channels_by_id.insert(
|
||||||
channel_proto.id,
|
channel_proto.id,
|
||||||
|
@ -112,7 +112,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
||||||
id: channel_proto.id,
|
id: channel_proto.id,
|
||||||
visibility: channel_proto.visibility(),
|
visibility: channel_proto.visibility(),
|
||||||
role: channel_proto.role(),
|
role: channel_proto.role(),
|
||||||
name: channel_proto.name,
|
name: channel_proto.name.into(),
|
||||||
unseen_note_version: None,
|
unseen_note_version: None,
|
||||||
unseen_message_id: None,
|
unseen_message_id: None,
|
||||||
parent_path: channel_proto.parent_path,
|
parent_path: channel_proto.parent_path,
|
||||||
|
@ -146,11 +146,11 @@ fn channel_path_sorting_key<'a>(
|
||||||
let (parent_path, name) = channels_by_id
|
let (parent_path, name) = channels_by_id
|
||||||
.get(&id)
|
.get(&id)
|
||||||
.map_or((&[] as &[_], None), |channel| {
|
.map_or((&[] as &[_], None), |channel| {
|
||||||
(channel.parent_path.as_slice(), Some(channel.name.as_str()))
|
(channel.parent_path.as_slice(), Some(channel.name.as_ref()))
|
||||||
});
|
});
|
||||||
parent_path
|
parent_path
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_str()))
|
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_ref()))
|
||||||
.chain(name)
|
.chain(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use call::ActiveCall;
|
||||||
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
||||||
use client::User;
|
use client::User;
|
||||||
use futures::future::try_join_all;
|
use futures::future::try_join_all;
|
||||||
use gpui::{BackgroundExecutor, Model, TestAppContext};
|
use gpui::{BackgroundExecutor, Model, SharedString, TestAppContext};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
proto::{self, ChannelRole},
|
proto::{self, ChannelRole},
|
||||||
RECEIVE_TIMEOUT,
|
RECEIVE_TIMEOUT,
|
||||||
|
@ -46,13 +46,13 @@ async fn test_core_channels(
|
||||||
&[
|
&[
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_b_id,
|
id: channel_b_id,
|
||||||
name: "channel-b".to_string(),
|
name: "channel-b".into(),
|
||||||
depth: 1,
|
depth: 1,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
|
@ -92,7 +92,7 @@ async fn test_core_channels(
|
||||||
cx_b,
|
cx_b,
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
}],
|
}],
|
||||||
|
@ -140,13 +140,13 @@ async fn test_core_channels(
|
||||||
&[
|
&[
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_b_id,
|
id: channel_b_id,
|
||||||
name: "channel-b".to_string(),
|
name: "channel-b".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
},
|
},
|
||||||
|
@ -168,19 +168,19 @@ async fn test_core_channels(
|
||||||
&[
|
&[
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_b_id,
|
id: channel_b_id,
|
||||||
name: "channel-b".to_string(),
|
name: "channel-b".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_c_id,
|
id: channel_c_id,
|
||||||
name: "channel-c".to_string(),
|
name: "channel-c".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
depth: 2,
|
depth: 2,
|
||||||
},
|
},
|
||||||
|
@ -211,19 +211,19 @@ async fn test_core_channels(
|
||||||
&[
|
&[
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_b_id,
|
id: channel_b_id,
|
||||||
name: "channel-b".to_string(),
|
name: "channel-b".into(),
|
||||||
depth: 1,
|
depth: 1,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
id: channel_c_id,
|
id: channel_c_id,
|
||||||
name: "channel-c".to_string(),
|
name: "channel-c".into(),
|
||||||
depth: 2,
|
depth: 2,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
|
@ -245,7 +245,7 @@ async fn test_core_channels(
|
||||||
cx_a,
|
cx_a,
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
}],
|
}],
|
||||||
|
@ -255,7 +255,7 @@ async fn test_core_channels(
|
||||||
cx_b,
|
cx_b,
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
}],
|
}],
|
||||||
|
@ -278,7 +278,7 @@ async fn test_core_channels(
|
||||||
cx_a,
|
cx_a,
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a".to_string(),
|
name: "channel-a".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
}],
|
}],
|
||||||
|
@ -309,7 +309,7 @@ async fn test_core_channels(
|
||||||
cx_a,
|
cx_a,
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
id: channel_a_id,
|
id: channel_a_id,
|
||||||
name: "channel-a-renamed".to_string(),
|
name: "channel-a-renamed".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
}],
|
}],
|
||||||
|
@ -418,7 +418,7 @@ async fn test_channel_room(
|
||||||
cx_b,
|
cx_b,
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
id: zed_id,
|
id: zed_id,
|
||||||
name: "zed".to_string(),
|
name: "zed".into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
}],
|
}],
|
||||||
|
@ -680,7 +680,7 @@ async fn test_permissions_update_while_invited(
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: rust_id,
|
id: rust_id,
|
||||||
name: "rust".to_string(),
|
name: "rust".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
@ -708,7 +708,7 @@ async fn test_permissions_update_while_invited(
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: rust_id,
|
id: rust_id,
|
||||||
name: "rust".to_string(),
|
name: "rust".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
@ -747,7 +747,7 @@ async fn test_channel_rename(
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: rust_id,
|
id: rust_id,
|
||||||
name: "rust-archive".to_string(),
|
name: "rust-archive".into(),
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
@ -759,7 +759,7 @@ async fn test_channel_rename(
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: rust_id,
|
id: rust_id,
|
||||||
name: "rust-archive".to_string(),
|
name: "rust-archive".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
@ -888,7 +888,7 @@ async fn test_lost_channel_creation(
|
||||||
&[ExpectedChannel {
|
&[ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: channel_id,
|
id: channel_id,
|
||||||
name: "x".to_string(),
|
name: "x".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
@ -912,13 +912,13 @@ async fn test_lost_channel_creation(
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: channel_id,
|
id: channel_id,
|
||||||
name: "x".to_string(),
|
name: "x".into(),
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 1,
|
depth: 1,
|
||||||
id: subchannel_id,
|
id: subchannel_id,
|
||||||
name: "subchannel".to_string(),
|
name: "subchannel".into(),
|
||||||
role: ChannelRole::Admin,
|
role: ChannelRole::Admin,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -943,13 +943,13 @@ async fn test_lost_channel_creation(
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: channel_id,
|
id: channel_id,
|
||||||
name: "x".to_string(),
|
name: "x".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 1,
|
depth: 1,
|
||||||
id: subchannel_id,
|
id: subchannel_id,
|
||||||
name: "subchannel".to_string(),
|
name: "subchannel".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1221,13 +1221,13 @@ async fn test_channel_membership_notifications(
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: zed_channel,
|
id: zed_channel,
|
||||||
name: "zed".to_string(),
|
name: "zed".into(),
|
||||||
role: ChannelRole::Guest,
|
role: ChannelRole::Guest,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 1,
|
depth: 1,
|
||||||
id: vim_channel,
|
id: vim_channel,
|
||||||
name: "vim".to_string(),
|
name: "vim".into(),
|
||||||
role: ChannelRole::Member,
|
role: ChannelRole::Member,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1250,13 +1250,13 @@ async fn test_channel_membership_notifications(
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
id: zed_channel,
|
id: zed_channel,
|
||||||
name: "zed".to_string(),
|
name: "zed".into(),
|
||||||
role: ChannelRole::Guest,
|
role: ChannelRole::Guest,
|
||||||
},
|
},
|
||||||
ExpectedChannel {
|
ExpectedChannel {
|
||||||
depth: 1,
|
depth: 1,
|
||||||
id: vim_channel,
|
id: vim_channel,
|
||||||
name: "vim".to_string(),
|
name: "vim".into(),
|
||||||
role: ChannelRole::Guest,
|
role: ChannelRole::Guest,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1476,7 +1476,7 @@ async fn test_channel_moving(
|
||||||
struct ExpectedChannel {
|
struct ExpectedChannel {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
id: ChannelId,
|
id: ChannelId,
|
||||||
name: String,
|
name: SharedString,
|
||||||
role: ChannelRole,
|
role: ChannelRole,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1515,7 +1515,7 @@ fn assert_channels(
|
||||||
.ordered_channels()
|
.ordered_channels()
|
||||||
.map(|(depth, channel)| ExpectedChannel {
|
.map(|(depth, channel)| ExpectedChannel {
|
||||||
depth,
|
depth,
|
||||||
name: channel.name.clone(),
|
name: channel.name.clone().into(),
|
||||||
id: channel.id,
|
id: channel.id,
|
||||||
role: channel.role,
|
role: channel.role,
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::db::ChannelRole;
|
||||||
use super::{run_randomized_test, RandomizedTest, TestClient, TestError, TestServer, UserTestPlan};
|
use super::{run_randomized_test, RandomizedTest, TestClient, TestError, TestServer, UserTestPlan};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use gpui::{BackgroundExecutor, TestAppContext};
|
use gpui::{BackgroundExecutor, SharedString, TestAppContext};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -30,13 +30,13 @@ struct RandomChannelBufferTest;
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
enum ChannelBufferOperation {
|
enum ChannelBufferOperation {
|
||||||
JoinChannelNotes {
|
JoinChannelNotes {
|
||||||
channel_name: String,
|
channel_name: SharedString,
|
||||||
},
|
},
|
||||||
LeaveChannelNotes {
|
LeaveChannelNotes {
|
||||||
channel_name: String,
|
channel_name: SharedString,
|
||||||
},
|
},
|
||||||
EditChannelNotes {
|
EditChannelNotes {
|
||||||
channel_name: String,
|
channel_name: SharedString,
|
||||||
edits: Vec<(Range<usize>, Arc<str>)>,
|
edits: Vec<(Range<usize>, Arc<str>)>,
|
||||||
},
|
},
|
||||||
Noop,
|
Noop,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,9 @@ use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
|
||||||
use client::UserId;
|
use client::UserId;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{AnchorRangeExt, Editor};
|
use editor::{AnchorRangeExt, Editor};
|
||||||
use gpui::{AnyView, AsyncAppContext, Model, Render, Task, View, ViewContext, WeakView};
|
use gpui::{
|
||||||
|
AnyView, AsyncWindowContext, Model, Render, SharedString, Task, View, ViewContext, WeakView,
|
||||||
|
};
|
||||||
use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
|
use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use project::search::SearchQuery;
|
use project::search::SearchQuery;
|
||||||
|
@ -46,8 +48,7 @@ impl MessageEditor {
|
||||||
cx.subscribe(&buffer, Self::on_buffer_event).detach();
|
cx.subscribe(&buffer, Self::on_buffer_event).detach();
|
||||||
|
|
||||||
let markdown = language_registry.language_for_name("Markdown");
|
let markdown = language_registry.language_for_name("Markdown");
|
||||||
cx.app_context()
|
cx.spawn(|_, mut cx| async move {
|
||||||
.spawn(|mut cx| async move {
|
|
||||||
let markdown = markdown.await?;
|
let markdown = markdown.await?;
|
||||||
buffer.update(&mut cx, |buffer, cx| {
|
buffer.update(&mut cx, |buffer, cx| {
|
||||||
buffer.set_language(Some(markdown), cx)
|
buffer.set_language(Some(markdown), cx)
|
||||||
|
@ -69,7 +70,7 @@ impl MessageEditor {
|
||||||
pub fn set_channel(
|
pub fn set_channel(
|
||||||
&mut self,
|
&mut self,
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
channel_name: Option<String>,
|
channel_name: Option<SharedString>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
|
@ -137,7 +138,9 @@ impl MessageEditor {
|
||||||
if let language::Event::Reparsed | language::Event::Edited = event {
|
if let language::Event::Reparsed | language::Event::Edited = event {
|
||||||
let buffer = buffer.read(cx).snapshot();
|
let buffer = buffer.read(cx).snapshot();
|
||||||
self.mentions_task = Some(cx.spawn(|this, cx| async move {
|
self.mentions_task = Some(cx.spawn(|this, cx| async move {
|
||||||
cx.background().timer(MENTIONS_DEBOUNCE_INTERVAL).await;
|
cx.background_executor()
|
||||||
|
.timer(MENTIONS_DEBOUNCE_INTERVAL)
|
||||||
|
.await;
|
||||||
Self::find_mentions(this, buffer, cx).await;
|
Self::find_mentions(this, buffer, cx).await;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -146,10 +149,10 @@ impl MessageEditor {
|
||||||
async fn find_mentions(
|
async fn find_mentions(
|
||||||
this: WeakView<MessageEditor>,
|
this: WeakView<MessageEditor>,
|
||||||
buffer: BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncWindowContext,
|
||||||
) {
|
) {
|
||||||
let (buffer, ranges) = cx
|
let (buffer, ranges) = cx
|
||||||
.background()
|
.background_executor()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
let ranges = MENTIONS_SEARCH.search(&buffer, None).await;
|
let ranges = MENTIONS_SEARCH.search(&buffer, None).await;
|
||||||
(buffer, ranges)
|
(buffer, ranges)
|
||||||
|
@ -186,6 +189,10 @@ impl MessageEditor {
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn focus_handle(&self, cx: &gpui::AppContext) -> gpui::FocusHandle {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for MessageEditor {
|
impl Render for MessageEditor {
|
||||||
|
@ -196,98 +203,98 @@ impl Render for MessageEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod tests {
|
// mod tests {
|
||||||
use super::*;
|
// use super::*;
|
||||||
use client::{Client, User, UserStore};
|
// use client::{Client, User, UserStore};
|
||||||
use gpui::{TestAppContext, WindowHandle};
|
// use gpui::{TestAppContext, WindowHandle};
|
||||||
use language::{Language, LanguageConfig};
|
// use language::{Language, LanguageConfig};
|
||||||
use rpc::proto;
|
// use rpc::proto;
|
||||||
use settings::SettingsStore;
|
// use settings::SettingsStore;
|
||||||
use util::{http::FakeHttpClient, test::marked_text_ranges};
|
// use util::{http::FakeHttpClient, test::marked_text_ranges};
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
async fn test_message_editor(cx: &mut TestAppContext) {
|
// async fn test_message_editor(cx: &mut TestAppContext) {
|
||||||
let editor = init_test(cx);
|
// let editor = init_test(cx);
|
||||||
let editor = editor.root(cx);
|
// let editor = editor.root(cx);
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
// editor.update(cx, |editor, cx| {
|
||||||
editor.set_members(
|
// editor.set_members(
|
||||||
vec![
|
// vec![
|
||||||
ChannelMembership {
|
// ChannelMembership {
|
||||||
user: Arc::new(User {
|
// user: Arc::new(User {
|
||||||
github_login: "a-b".into(),
|
// github_login: "a-b".into(),
|
||||||
id: 101,
|
// id: 101,
|
||||||
avatar: None,
|
// avatar: None,
|
||||||
}),
|
// }),
|
||||||
kind: proto::channel_member::Kind::Member,
|
// kind: proto::channel_member::Kind::Member,
|
||||||
role: proto::ChannelRole::Member,
|
// role: proto::ChannelRole::Member,
|
||||||
},
|
// },
|
||||||
ChannelMembership {
|
// ChannelMembership {
|
||||||
user: Arc::new(User {
|
// user: Arc::new(User {
|
||||||
github_login: "C_D".into(),
|
// github_login: "C_D".into(),
|
||||||
id: 102,
|
// id: 102,
|
||||||
avatar: None,
|
// avatar: None,
|
||||||
}),
|
// }),
|
||||||
kind: proto::channel_member::Kind::Member,
|
// kind: proto::channel_member::Kind::Member,
|
||||||
role: proto::ChannelRole::Member,
|
// role: proto::ChannelRole::Member,
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
cx,
|
// cx,
|
||||||
);
|
// );
|
||||||
|
|
||||||
editor.editor.update(cx, |editor, cx| {
|
// editor.editor.update(cx, |editor, cx| {
|
||||||
editor.set_text("Hello, @a-b! Have you met @C_D?", cx)
|
// editor.set_text("Hello, @a-b! Have you met @C_D?", cx)
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
cx.foreground().advance_clock(MENTIONS_DEBOUNCE_INTERVAL);
|
// cx.foreground().advance_clock(MENTIONS_DEBOUNCE_INTERVAL);
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
// editor.update(cx, |editor, cx| {
|
||||||
let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false);
|
// let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
editor.take_message(cx),
|
// editor.take_message(cx),
|
||||||
MessageParams {
|
// MessageParams {
|
||||||
text,
|
// text,
|
||||||
mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
|
// mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn init_test(cx: &mut TestAppContext) -> WindowHandle<MessageEditor> {
|
// fn init_test(cx: &mut TestAppContext) -> WindowHandle<MessageEditor> {
|
||||||
cx.foreground().forbid_parking();
|
// cx.foreground().forbid_parking();
|
||||||
|
|
||||||
cx.update(|cx| {
|
// cx.update(|cx| {
|
||||||
let http = FakeHttpClient::with_404_response();
|
// let http = FakeHttpClient::with_404_response();
|
||||||
let client = Client::new(http.clone(), cx);
|
// let client = Client::new(http.clone(), cx);
|
||||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
// let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||||
cx.set_global(SettingsStore::test(cx));
|
// cx.set_global(SettingsStore::test(cx));
|
||||||
theme::init((), cx);
|
// theme::init((), cx);
|
||||||
language::init(cx);
|
// language::init(cx);
|
||||||
editor::init(cx);
|
// editor::init(cx);
|
||||||
client::init(&client, cx);
|
// client::init(&client, cx);
|
||||||
channel::init(&client, user_store, cx);
|
// channel::init(&client, user_store, cx);
|
||||||
});
|
// });
|
||||||
|
|
||||||
let language_registry = Arc::new(LanguageRegistry::test());
|
// let language_registry = Arc::new(LanguageRegistry::test());
|
||||||
language_registry.add(Arc::new(Language::new(
|
// language_registry.add(Arc::new(Language::new(
|
||||||
LanguageConfig {
|
// LanguageConfig {
|
||||||
name: "Markdown".into(),
|
// name: "Markdown".into(),
|
||||||
..Default::default()
|
// ..Default::default()
|
||||||
},
|
// },
|
||||||
Some(tree_sitter_markdown::language()),
|
// Some(tree_sitter_markdown::language()),
|
||||||
)));
|
// )));
|
||||||
|
|
||||||
let editor = cx.add_window(|cx| {
|
// let editor = cx.add_window(|cx| {
|
||||||
MessageEditor::new(
|
// MessageEditor::new(
|
||||||
language_registry,
|
// language_registry,
|
||||||
ChannelStore::global(cx),
|
// ChannelStore::global(cx),
|
||||||
cx.add_view(|cx| Editor::auto_height(4, cx)),
|
// cx.add_view(|cx| Editor::auto_height(4, cx)),
|
||||||
cx,
|
// cx,
|
||||||
)
|
// )
|
||||||
});
|
// });
|
||||||
cx.foreground().run_until_parked();
|
// cx.foreground().run_until_parked();
|
||||||
editor
|
// editor
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -852,7 +852,7 @@ impl CollabPanel {
|
||||||
.extend(channel_store.ordered_channels().enumerate().map(
|
.extend(channel_store.ordered_channels().enumerate().map(
|
||||||
|(ix, (_, channel))| StringMatchCandidate {
|
|(ix, (_, channel))| StringMatchCandidate {
|
||||||
id: ix,
|
id: ix,
|
||||||
string: channel.name.clone(),
|
string: channel.name.clone().into(),
|
||||||
char_bag: channel.name.chars().collect(),
|
char_bag: channel.name.chars().collect(),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -2262,7 +2262,7 @@ impl CollabPanel {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(channel.name.as_str())
|
Some(channel.name.as_ref())
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(name) = channel_name {
|
if let Some(name) = channel_name {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use std::{rc::Rc, sync::Arc};
|
||||||
use call::{report_call_event_for_room, ActiveCall, Room};
|
use call::{report_call_event_for_room, ActiveCall, Room};
|
||||||
pub use collab_panel::CollabPanel;
|
pub use collab_panel::CollabPanel;
|
||||||
pub use collab_titlebar_item::CollabTitlebarItem;
|
pub use collab_titlebar_item::CollabTitlebarItem;
|
||||||
|
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowBounds,
|
actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowBounds,
|
||||||
WindowKind, WindowOptions,
|
WindowKind, WindowOptions,
|
||||||
|
@ -157,6 +158,6 @@ fn notification_window_options(
|
||||||
// .into_any()
|
// .into_any()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
|
fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
|
||||||
// cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
|
cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
|
||||||
// }
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use smallvec::SmallVec;
|
||||||
use smol::future::FutureExt;
|
use smol::future::FutureExt;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub use test_context::*;
|
pub use test_context::*;
|
||||||
|
use time::UtcOffset;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
|
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
|
||||||
|
@ -536,6 +537,10 @@ impl AppContext {
|
||||||
self.platform.restart()
|
self.platform.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn local_timezone(&self) -> UtcOffset {
|
||||||
|
self.platform.local_timezone()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn push_effect(&mut self, effect: Effect) {
|
pub(crate) fn push_effect(&mut self, effect: Effect) {
|
||||||
match &effect {
|
match &effect {
|
||||||
Effect::Notify { emitter } => {
|
Effect::Notify { emitter } => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
px, AnyElement, AvailableSpace, BorrowAppContext, DispatchPhase, Element, IntoElement, Pixels,
|
px, AnyElement, AvailableSpace, BorrowAppContext, DispatchPhase, Element, IntoElement, Pixels,
|
||||||
Point, ScrollWheelEvent, Size, Style, StyleRefinement, ViewContext, WindowContext,
|
Point, ScrollWheelEvent, Size, Style, StyleRefinement, WindowContext,
|
||||||
};
|
};
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||||
|
@ -26,14 +26,14 @@ struct StateInner {
|
||||||
render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
|
render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
|
||||||
items: SumTree<ListItem>,
|
items: SumTree<ListItem>,
|
||||||
logical_scroll_top: Option<ListOffset>,
|
logical_scroll_top: Option<ListOffset>,
|
||||||
orientation: Orientation,
|
alignment: ListAlignment,
|
||||||
overdraw: Pixels,
|
overdraw: Pixels,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
|
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum Orientation {
|
pub enum ListAlignment {
|
||||||
Top,
|
Top,
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
@ -70,28 +70,23 @@ struct UnrenderedCount(usize);
|
||||||
struct Height(Pixels);
|
struct Height(Pixels);
|
||||||
|
|
||||||
impl ListState {
|
impl ListState {
|
||||||
pub fn new<F, V>(
|
pub fn new<F>(
|
||||||
element_count: usize,
|
element_count: usize,
|
||||||
orientation: Orientation,
|
orientation: ListAlignment,
|
||||||
overdraw: Pixels,
|
overdraw: Pixels,
|
||||||
cx: &mut ViewContext<V>,
|
render_item: F,
|
||||||
mut render_item: F,
|
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(&mut V, usize, &mut ViewContext<V>) -> AnyElement,
|
F: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
|
||||||
V: 'static,
|
|
||||||
{
|
{
|
||||||
let mut items = SumTree::new();
|
let mut items = SumTree::new();
|
||||||
items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
||||||
let view = cx.view().clone();
|
|
||||||
Self(Rc::new(RefCell::new(StateInner {
|
Self(Rc::new(RefCell::new(StateInner {
|
||||||
last_layout_width: None,
|
last_layout_width: None,
|
||||||
render_item: Box::new(move |ix, cx| {
|
render_item: Box::new(render_item),
|
||||||
view.update(cx, |view, cx| render_item(view, ix, cx))
|
|
||||||
}),
|
|
||||||
items,
|
items,
|
||||||
logical_scroll_top: None,
|
logical_scroll_top: None,
|
||||||
orientation,
|
alignment: orientation,
|
||||||
overdraw,
|
overdraw,
|
||||||
scroll_handler: None,
|
scroll_handler: None,
|
||||||
})))
|
})))
|
||||||
|
@ -179,7 +174,7 @@ impl StateInner {
|
||||||
.max(px(0.))
|
.max(px(0.))
|
||||||
.min(scroll_max);
|
.min(scroll_max);
|
||||||
|
|
||||||
if self.orientation == Orientation::Bottom && new_scroll_top == scroll_max {
|
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
|
||||||
self.logical_scroll_top = None;
|
self.logical_scroll_top = None;
|
||||||
} else {
|
} else {
|
||||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||||
|
@ -208,12 +203,12 @@ impl StateInner {
|
||||||
|
|
||||||
fn logical_scroll_top(&self) -> ListOffset {
|
fn logical_scroll_top(&self) -> ListOffset {
|
||||||
self.logical_scroll_top
|
self.logical_scroll_top
|
||||||
.unwrap_or_else(|| match self.orientation {
|
.unwrap_or_else(|| match self.alignment {
|
||||||
Orientation::Top => ListOffset {
|
ListAlignment::Top => ListOffset {
|
||||||
item_ix: 0,
|
item_ix: 0,
|
||||||
offset_in_item: px(0.),
|
offset_in_item: px(0.),
|
||||||
},
|
},
|
||||||
Orientation::Bottom => ListOffset {
|
ListAlignment::Bottom => ListOffset {
|
||||||
item_ix: self.items.summary().count,
|
item_ix: self.items.summary().count,
|
||||||
offset_in_item: px(0.),
|
offset_in_item: px(0.),
|
||||||
},
|
},
|
||||||
|
@ -344,12 +339,12 @@ impl Element for List {
|
||||||
offset_in_item: rendered_height - bounds.size.height,
|
offset_in_item: rendered_height - bounds.size.height,
|
||||||
};
|
};
|
||||||
|
|
||||||
match state.orientation {
|
match state.alignment {
|
||||||
Orientation::Top => {
|
ListAlignment::Top => {
|
||||||
scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.));
|
scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.));
|
||||||
state.logical_scroll_top = Some(scroll_top);
|
state.logical_scroll_top = Some(scroll_top);
|
||||||
}
|
}
|
||||||
Orientation::Bottom => {
|
ListAlignment::Bottom => {
|
||||||
scroll_top = ListOffset {
|
scroll_top = ListOffset {
|
||||||
item_ix: cursor.start().0,
|
item_ix: cursor.start().0,
|
||||||
offset_in_item: rendered_height - bounds.size.height,
|
offset_in_item: rendered_height - bounds.size.height,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod action;
|
mod action;
|
||||||
mod app;
|
mod app;
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
mod color;
|
mod color;
|
||||||
mod element;
|
mod element;
|
||||||
|
@ -15,6 +16,7 @@ mod keymap;
|
||||||
mod platform;
|
mod platform;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
mod scene;
|
mod scene;
|
||||||
|
mod shared_string;
|
||||||
mod style;
|
mod style;
|
||||||
mod styled;
|
mod styled;
|
||||||
mod subscription;
|
mod subscription;
|
||||||
|
@ -57,6 +59,7 @@ pub use scene::*;
|
||||||
pub use serde;
|
pub use serde;
|
||||||
pub use serde_derive;
|
pub use serde_derive;
|
||||||
pub use serde_json;
|
pub use serde_json;
|
||||||
|
pub use shared_string::*;
|
||||||
pub use smallvec;
|
pub use smallvec;
|
||||||
pub use smol::Timer;
|
pub use smol::Timer;
|
||||||
pub use style::*;
|
pub use style::*;
|
||||||
|
@ -76,6 +79,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::{Borrow, BorrowMut},
|
borrow::{Borrow, BorrowMut},
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use taffy::TaffyLayoutEngine;
|
use taffy::TaffyLayoutEngine;
|
||||||
|
|
||||||
|
@ -210,61 +214,3 @@ impl<T> Flatten<T> for Result<T> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
|
|
||||||
pub struct SharedString(ArcCow<'static, str>);
|
|
||||||
|
|
||||||
impl Default for SharedString {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(ArcCow::Owned("".into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for SharedString {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Borrow<str> for SharedString {
|
|
||||||
fn borrow(&self) -> &str {
|
|
||||||
self.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for SharedString {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for SharedString {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.0.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Self(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for SharedString {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(self.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for SharedString {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let s = String::deserialize(deserializer)?;
|
|
||||||
Ok(SharedString::from(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
101
crates/gpui2/src/shared_string.rs
Normal file
101
crates/gpui2/src/shared_string.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{borrow::Borrow, sync::Arc};
|
||||||
|
use util::arc_cow::ArcCow;
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
|
||||||
|
pub struct SharedString(ArcCow<'static, str>);
|
||||||
|
|
||||||
|
impl Default for SharedString {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(ArcCow::Owned("".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for SharedString {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<str> for SharedString {
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for SharedString {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SharedString {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<String> for SharedString {
|
||||||
|
fn eq(&self, other: &String) -> bool {
|
||||||
|
self.as_ref() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<SharedString> for String {
|
||||||
|
fn eq(&self, other: &SharedString) -> bool {
|
||||||
|
self == other.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<str> for SharedString {
|
||||||
|
fn eq(&self, other: &str) -> bool {
|
||||||
|
self.as_ref() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq<&'a str> for SharedString {
|
||||||
|
fn eq(&self, other: &&'a str) -> bool {
|
||||||
|
self.as_ref() == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Arc<str>> for SharedString {
|
||||||
|
fn into(self) -> Arc<str> {
|
||||||
|
match self.0 {
|
||||||
|
ArcCow::Borrowed(borrowed) => Arc::from(borrowed),
|
||||||
|
ArcCow::Owned(owned) => owned.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for SharedString {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.0.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SharedString {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SharedString {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
Ok(SharedString::from(s))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue