Add ChannelList to AppState

This commit is contained in:
Max Brunsfeld 2021-08-23 15:02:42 -07:00
parent 43bb38206f
commit 5ecedd894d
12 changed files with 134 additions and 93 deletions

View file

@ -106,6 +106,7 @@ impl Element for List {
} }
fn paint(&mut self, bounds: RectF, _: &mut (), cx: &mut PaintContext) { fn paint(&mut self, bounds: RectF, _: &mut (), cx: &mut PaintContext) {
cx.scene.push_layer(Some(bounds));
let state = &mut *self.state.0.lock(); let state = &mut *self.state.0.lock();
let visible_range = state.visible_range(bounds.height()); let visible_range = state.visible_range(bounds.height());
@ -119,6 +120,7 @@ impl Element for List {
element.paint(origin, cx); element.paint(origin, cx);
item_top += element.size().y(); item_top += element.size().y();
} }
cx.scene.pop_layer();
} }
fn dispatch_event( fn dispatch_event(

View file

@ -1425,12 +1425,13 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let channels_a = ChannelList::new(client_a, &mut cx_a.to_async()) let channels_a = cx_a.add_model(|cx| ChannelList::new(client_a, cx));
.await channels_a
.unwrap(); .condition(&mut cx_a, |list, _| list.available_channels().is_some())
.await;
channels_a.read_with(&cx_a, |list, _| { channels_a.read_with(&cx_a, |list, _| {
assert_eq!( assert_eq!(
list.available_channels(), list.available_channels().unwrap(),
&[ChannelDetails { &[ChannelDetails {
id: channel_id.to_proto(), id: channel_id.to_proto(),
name: "test-channel".to_string() name: "test-channel".to_string()
@ -1448,12 +1449,13 @@ mod tests {
}) })
.await; .await;
let channels_b = ChannelList::new(client_b, &mut cx_b.to_async()) let channels_b = cx_b.add_model(|cx| ChannelList::new(client_b, cx));
.await channels_b
.unwrap(); .condition(&mut cx_b, |list, _| list.available_channels().is_some())
.await;
channels_b.read_with(&cx_b, |list, _| { channels_b.read_with(&cx_b, |list, _| {
assert_eq!( assert_eq!(
list.available_channels(), list.available_channels().unwrap(),
&[ChannelDetails { &[ChannelDetails {
id: channel_id.to_proto(), id: channel_id.to_proto(),
name: "test-channel".to_string() name: "test-channel".to_string()

View file

@ -3,9 +3,7 @@ use crate::{
util::log_async_errors, util::log_async_errors,
}; };
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use gpui::{ use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, WeakModelHandle};
AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, WeakModelHandle,
};
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{hash_map, BTreeSet, HashMap}, collections::{hash_map, BTreeSet, HashMap},
@ -17,7 +15,7 @@ use zrpc::{
}; };
pub struct ChannelList { pub struct ChannelList {
available_channels: Vec<ChannelDetails>, available_channels: Option<Vec<ChannelDetails>>,
channels: HashMap<u64, WeakModelHandle<Channel>>, channels: HashMap<u64, WeakModelHandle<Channel>>,
rpc: Arc<Client>, rpc: Arc<Client>,
} }
@ -55,21 +53,32 @@ impl Entity for ChannelList {
} }
impl ChannelList { impl ChannelList {
pub async fn new(rpc: Arc<rpc::Client>, cx: &mut AsyncAppContext) -> Result<ModelHandle<Self>> { pub fn new(rpc: Arc<rpc::Client>, cx: &mut ModelContext<Self>) -> Self {
cx.spawn(|this, mut cx| {
let rpc = rpc.clone();
log_async_errors(async move {
let response = rpc let response = rpc
.request(proto::GetChannels {}) .request(proto::GetChannels {})
.await .await
.context("failed to fetch available channels")?; .context("failed to fetch available channels")?;
this.update(&mut cx, |this, cx| {
Ok(cx.add_model(|_| Self { this.available_channels =
available_channels: response.channels.into_iter().map(Into::into).collect(), Some(response.channels.into_iter().map(Into::into).collect());
cx.notify();
});
Ok(())
})
})
.detach();
Self {
available_channels: None,
channels: Default::default(), channels: Default::default(),
rpc, rpc,
})) }
} }
pub fn available_channels(&self) -> &[ChannelDetails] { pub fn available_channels(&self) -> Option<&[ChannelDetails]> {
&self.available_channels self.available_channels.as_ref().map(Vec::as_slice)
} }
pub fn get_channel( pub fn get_channel(
@ -82,8 +91,8 @@ impl ChannelList {
hash_map::Entry::Vacant(entry) => { hash_map::Entry::Vacant(entry) => {
if let Some(details) = self if let Some(details) = self
.available_channels .available_channels
.iter() .as_ref()
.find(|details| details.id == id) .and_then(|channels| channels.iter().find(|details| details.id == id))
{ {
let rpc = self.rpc.clone(); let rpc = self.rpc.clone();
let channel = cx.add_model(|cx| Channel::new(details.clone(), rpc, cx)); let channel = cx.add_model(|cx| Channel::new(details.clone(), rpc, cx));

View file

@ -1,38 +1,48 @@
use crate::Settings; use super::{
channel::{Channel, ChannelList},
use super::channel::{Channel, ChannelList}; Settings,
};
use gpui::{elements::*, Entity, ModelHandle, RenderContext, View, ViewContext}; use gpui::{elements::*, Entity, ModelHandle, RenderContext, View, ViewContext};
use postage::watch; use postage::watch;
pub struct ChatPanel { pub struct ChatPanel {
// channel_list: ModelHandle<ChannelList>, channel_list: ModelHandle<ChannelList>,
// active_channel: Option<ModelHandle<Channel>>, active_channel: Option<ModelHandle<Channel>>,
// active_channel_subscription: Subscription,
messages: ListState, messages: ListState,
} }
pub enum Event {} pub enum Event {}
impl ChatPanel { impl ChatPanel {
pub fn new(settings: watch::Receiver<Settings>) -> Self { pub fn new(
let settings = settings.borrow(); channel_list: ModelHandle<ChannelList>,
let mut messages = Vec::new(); settings: watch::Receiver<Settings>,
for i in 0..1000 { cx: &mut ViewContext<Self>,
messages.push( ) -> Self {
Container::new( let mut this = Self {
Label::new( channel_list,
format!("This is message {}", i), messages: ListState::new(Vec::new()),
settings.ui_font_family, active_channel: None,
settings.ui_font_size, };
) let channel = this.channel_list.update(cx, |list, cx| {
.with_style(&settings.theme.selector.label) if let Some(channel_id) = list
.boxed(), .available_channels()
) .and_then(|channels| channels.first())
.boxed(), .map(|details| details.id)
); {
return list.get_channel(channel_id, cx);
} }
Self { None
messages: ListState::new(messages), });
if let Some(channel) = channel {
this.set_active_channel(channel);
} }
this
}
pub fn set_active_channel(&mut self, channel: ModelHandle<Channel>) {
//
} }
} }

View file

@ -2596,8 +2596,9 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
editor::Point, editor::Point,
language::LanguageRegistry,
settings, settings,
test::{build_app_state, sample_text}, test::{build_settings, sample_text},
}; };
use buffer::History; use buffer::History;
use unindent::Unindent; use unindent::Unindent;
@ -4120,8 +4121,9 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) { async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
let app_state = cx.read(build_app_state); let settings = cx.read(build_settings);
let lang = app_state.languages.select_language("z.rs"); let languages = LanguageRegistry::new();
let lang = languages.select_language("z.rs");
let text = r#" let text = r#"
use mod1::mod2::{mod3, mod4}; use mod1::mod2::{mod3, mod4};
@ -4134,8 +4136,7 @@ mod tests {
let history = History::new(text.into()); let history = History::new(text.into());
Buffer::from_history(0, history, None, lang.cloned(), cx) Buffer::from_history(0, history, None, lang.cloned(), cx)
}); });
let (_, view) = let (_, view) = cx.add_window(|cx| Editor::for_buffer(buffer, settings.clone(), cx));
cx.add_window(|cx| Editor::for_buffer(buffer, app_state.settings.clone(), cx));
view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing()) view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing())
.await; .await;

View file

@ -2641,7 +2641,7 @@ impl<'a> Into<proto::Operation> for &'a Operation {
}, },
), ),
#[cfg(test)] #[cfg(test)]
Operation::Test(_) => unimplemented!() Operation::Test(_) => unimplemented!(),
}), }),
} }
} }
@ -2895,7 +2895,8 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
fs::RealFs, fs::RealFs,
test::{build_app_state, temp_tree}, language::LanguageRegistry,
test::temp_tree,
util::RandomCharIter, util::RandomCharIter,
worktree::{Worktree, WorktreeHandle as _}, worktree::{Worktree, WorktreeHandle as _},
}; };
@ -3825,8 +3826,8 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_reparse(mut cx: gpui::TestAppContext) { async fn test_reparse(mut cx: gpui::TestAppContext) {
let app_state = cx.read(build_app_state); let languages = LanguageRegistry::new();
let rust_lang = app_state.languages.select_language("test.rs"); let rust_lang = languages.select_language("test.rs");
assert!(rust_lang.is_some()); assert!(rust_lang.is_some());
let buffer = cx.add_model(|cx| { let buffer = cx.add_model(|cx| {
@ -3966,8 +3967,8 @@ mod tests {
async fn test_enclosing_bracket_ranges(mut cx: gpui::TestAppContext) { async fn test_enclosing_bracket_ranges(mut cx: gpui::TestAppContext) {
use unindent::Unindent as _; use unindent::Unindent as _;
let app_state = cx.read(build_app_state); let languages = LanguageRegistry::new();
let rust_lang = app_state.languages.select_language("test.rs"); let rust_lang = languages.select_language("test.rs");
assert!(rust_lang.is_some()); assert!(rust_lang.is_some());
let buffer = cx.add_model(|cx| { let buffer = cx.add_model(|cx| {

View file

@ -455,7 +455,7 @@ mod tests {
editor::init(cx); editor::init(cx);
}); });
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace
.update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {
@ -515,7 +515,7 @@ mod tests {
) )
.await; .await;
let mut app_state = cx.read(build_app_state); let mut app_state = cx.update(build_app_state);
Arc::get_mut(&mut app_state).unwrap().fs = fs; Arc::get_mut(&mut app_state).unwrap().fs = fs;
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
@ -577,7 +577,7 @@ mod tests {
fs::create_dir(&dir_path).unwrap(); fs::create_dir(&dir_path).unwrap();
fs::write(&file_path, "").unwrap(); fs::write(&file_path, "").unwrap();
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace
.update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {
@ -624,7 +624,7 @@ mod tests {
"dir2": { "a.txt": "" } "dir2": { "a.txt": "" }
})); }));
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace

View file

@ -19,7 +19,8 @@ mod util;
pub mod workspace; pub mod workspace;
pub mod worktree; pub mod worktree;
use gpui::action; use channel::ChannelList;
use gpui::{action, ModelHandle};
pub use settings::Settings; pub use settings::Settings;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -36,6 +37,7 @@ pub struct AppState {
pub themes: Arc<settings::ThemeRegistry>, pub themes: Arc<settings::ThemeRegistry>,
pub rpc: Arc<rpc::Client>, pub rpc: Arc<rpc::Client>,
pub fs: Arc<dyn fs::Fs>, pub fs: Arc<dyn fs::Fs>,
pub channel_list: ModelHandle<ChannelList>,
} }
pub fn init(cx: &mut gpui::MutableAppContext) { pub fn init(cx: &mut gpui::MutableAppContext) {

View file

@ -7,7 +7,9 @@ use parking_lot::Mutex;
use simplelog::SimpleLogger; use simplelog::SimpleLogger;
use std::{fs, path::PathBuf, sync::Arc}; use std::{fs, path::PathBuf, sync::Arc};
use zed::{ use zed::{
self, assets, editor, file_finder, self, assets,
channel::ChannelList,
editor, file_finder,
fs::RealFs, fs::RealFs,
language, menus, rpc, settings, theme_selector, language, menus, rpc, settings, theme_selector,
workspace::{self, OpenParams, OpenPaths}, workspace::{self, OpenParams, OpenPaths},
@ -25,17 +27,17 @@ fn main() {
let languages = Arc::new(language::LanguageRegistry::new()); let languages = Arc::new(language::LanguageRegistry::new());
languages.set_theme(&settings.borrow().theme); languages.set_theme(&settings.borrow().theme);
let app_state = AppState { app.run(move |cx| {
let rpc = rpc::Client::new();
let app_state = Arc::new(AppState {
languages: languages.clone(), languages: languages.clone(),
settings_tx: Arc::new(Mutex::new(settings_tx)), settings_tx: Arc::new(Mutex::new(settings_tx)),
settings, settings,
themes, themes,
rpc: rpc::Client::new(), channel_list: cx.add_model(|cx| ChannelList::new(rpc.clone(), cx)),
rpc,
fs: Arc::new(RealFs), fs: Arc::new(RealFs),
}; });
app.run(move |cx| {
let app_state = Arc::new(app_state);
zed::init(cx); zed::init(cx);
workspace::init(cx); workspace::init(cx);

View file

@ -1,13 +1,15 @@
use crate::{ use crate::{
channel::ChannelList,
fs::RealFs, fs::RealFs,
language::LanguageRegistry, language::LanguageRegistry,
rpc, rpc,
settings::{self, ThemeRegistry}, settings::{self, ThemeRegistry},
time::ReplicaId, time::ReplicaId,
AppState, AppState, Settings,
}; };
use gpui::{AppContext, Entity, ModelHandle}; use gpui::{AppContext, Entity, ModelHandle, MutableAppContext};
use parking_lot::Mutex; use parking_lot::Mutex;
use postage::watch;
use smol::channel; use smol::channel;
use std::{ use std::{
marker::PhantomData, marker::PhantomData,
@ -153,16 +155,22 @@ fn write_tree(path: &Path, tree: serde_json::Value) {
} }
} }
pub fn build_app_state(cx: &AppContext) -> Arc<AppState> { pub fn build_settings(cx: &AppContext) -> watch::Receiver<Settings> {
settings::channel(&cx.font_cache()).unwrap().1
}
pub fn build_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
let (settings_tx, settings) = settings::channel(&cx.font_cache()).unwrap(); let (settings_tx, settings) = settings::channel(&cx.font_cache()).unwrap();
let languages = Arc::new(LanguageRegistry::new()); let languages = Arc::new(LanguageRegistry::new());
let themes = ThemeRegistry::new(()); let themes = ThemeRegistry::new(());
let rpc = rpc::Client::new();
Arc::new(AppState { Arc::new(AppState {
settings_tx: Arc::new(Mutex::new(settings_tx)), settings_tx: Arc::new(Mutex::new(settings_tx)),
settings, settings,
themes, themes,
languages: languages.clone(), languages: languages.clone(),
rpc: rpc::Client::new(), channel_list: cx.add_model(|cx| ChannelList::new(rpc.clone(), cx)),
rpc,
fs: Arc::new(RealFs), fs: Arc::new(RealFs),
}) })
} }

View file

@ -372,7 +372,13 @@ impl Workspace {
let mut right_sidebar = Sidebar::new(Side::Right); let mut right_sidebar = Sidebar::new(Side::Right);
right_sidebar.add_item( right_sidebar.add_item(
"icons/comment-16.svg", "icons/comment-16.svg",
cx.add_view(|_| ChatPanel::new(app_state.settings.clone())) cx.add_view(|cx| {
ChatPanel::new(
app_state.channel_list.clone(),
app_state.settings.clone(),
cx,
)
})
.into(), .into(),
); );
right_sidebar.add_item("icons/user-16.svg", cx.add_view(|_| ProjectBrowser).into()); right_sidebar.add_item("icons/user-16.svg", cx.add_view(|_| ProjectBrowser).into());
@ -1018,7 +1024,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_open_paths_action(mut cx: gpui::TestAppContext) { async fn test_open_paths_action(mut cx: gpui::TestAppContext) {
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let dir = temp_tree(json!({ let dir = temp_tree(json!({
"a": { "a": {
"aa": null, "aa": null,
@ -1091,7 +1097,7 @@ mod tests {
}, },
})); }));
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace
@ -1195,7 +1201,7 @@ mod tests {
fs.insert_file("/dir1/a.txt", "".into()).await.unwrap(); fs.insert_file("/dir1/a.txt", "".into()).await.unwrap();
fs.insert_file("/dir2/b.txt", "".into()).await.unwrap(); fs.insert_file("/dir2/b.txt", "".into()).await.unwrap();
let mut app_state = cx.read(build_app_state); let mut app_state = cx.update(build_app_state);
Arc::get_mut(&mut app_state).unwrap().fs = Arc::new(fs); Arc::get_mut(&mut app_state).unwrap().fs = Arc::new(fs);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
@ -1264,7 +1270,7 @@ mod tests {
"a.txt": "", "a.txt": "",
})); }));
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace
.update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {
@ -1309,7 +1315,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_open_and_save_new_file(mut cx: gpui::TestAppContext) { async fn test_open_and_save_new_file(mut cx: gpui::TestAppContext) {
let dir = TempDir::new("test-new-file").unwrap(); let dir = TempDir::new("test-new-file").unwrap();
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace
.update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {
@ -1408,7 +1414,7 @@ mod tests {
async fn test_new_empty_workspace(mut cx: gpui::TestAppContext) { async fn test_new_empty_workspace(mut cx: gpui::TestAppContext) {
cx.update(init); cx.update(init);
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
cx.dispatch_global_action(OpenNew(app_state)); cx.dispatch_global_action(OpenNew(app_state));
let window_id = *cx.window_ids().first().unwrap(); let window_id = *cx.window_ids().first().unwrap();
let workspace = cx.root_view::<Workspace>(window_id).unwrap(); let workspace = cx.root_view::<Workspace>(window_id).unwrap();
@ -1454,7 +1460,7 @@ mod tests {
}, },
})); }));
let app_state = cx.read(build_app_state); let app_state = cx.update(build_app_state);
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx)); let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
workspace workspace
.update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {

View file

@ -2640,13 +2640,12 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_save_file(mut cx: gpui::TestAppContext) { async fn test_save_file(mut cx: gpui::TestAppContext) {
let app_state = cx.read(build_app_state);
let dir = temp_tree(json!({ let dir = temp_tree(json!({
"file1": "the old contents", "file1": "the old contents",
})); }));
let tree = Worktree::open_local( let tree = Worktree::open_local(
dir.path(), dir.path(),
app_state.languages.clone(), Arc::new(LanguageRegistry::new()),
Arc::new(RealFs), Arc::new(RealFs),
&mut cx.to_async(), &mut cx.to_async(),
) )
@ -2668,7 +2667,6 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) { async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) {
let app_state = cx.read(build_app_state);
let dir = temp_tree(json!({ let dir = temp_tree(json!({
"file1": "the old contents", "file1": "the old contents",
})); }));
@ -2676,7 +2674,7 @@ mod tests {
let tree = Worktree::open_local( let tree = Worktree::open_local(
file_path.clone(), file_path.clone(),
app_state.languages.clone(), Arc::new(LanguageRegistry::new()),
Arc::new(RealFs), Arc::new(RealFs),
&mut cx.to_async(), &mut cx.to_async(),
) )