Skip to content

Commit 7e392ba

Browse files
authored
Channel History (#33)
* started work on new datastructures + cache * incomplete * finish async list & async list cache * Finished discord layer * fix rendering bugs * small changes * scrolling *kinda* works * first functional commit * de-genericize async_list * message grouping works again * Fixed random scrolling up block * scrolling updates * update the way that list state is invalidated * finalize * cleanup & prepare for PR * fix: make cargo clippy happy
1 parent 9c8cb4b commit 7e392ba

File tree

22 files changed

+1416
-131
lines changed

22 files changed

+1416
-131
lines changed

Cargo.lock

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
[workspace]
22
resolver = "2"
3-
members = [
4-
"src/ui"
5-
]
3+
members = ["src/ui", "src/cache", "src/chat", "src/discord"]
64

75
[workspace.dependencies]
86
chrono = "0.4.38"

src/cache/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "scope-backend-cache"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
gpui = { git = "https://github.com/huacnlee/zed.git", branch = "export-platform-window", default-features = false, features = [
8+
"http_client",
9+
"font-kit",
10+
] }
11+
rand = "0.8.5"
12+
scope-chat = { version = "0.1.0", path = "../chat" }
13+
tokio = "1.41.1"

src/cache/src/async_list/mod.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
pub mod refcache;
2+
pub mod refcacheslice;
3+
pub mod tests;
4+
5+
use std::collections::HashMap;
6+
7+
use refcache::CacheReferences;
8+
use refcacheslice::Exists;
9+
use scope_chat::async_list::{AsyncListIndex, AsyncListItem, AsyncListResult};
10+
11+
pub struct AsyncListCache<I: AsyncListItem> {
12+
cache_refs: CacheReferences<I::Identifier>,
13+
cache_map: HashMap<I::Identifier, I>,
14+
}
15+
16+
impl<I: AsyncListItem> Default for AsyncListCache<I> {
17+
fn default() -> Self {
18+
Self::new()
19+
}
20+
}
21+
22+
impl<I: AsyncListItem> AsyncListCache<I> {
23+
pub fn new() -> Self {
24+
Self {
25+
cache_refs: CacheReferences::new(),
26+
cache_map: HashMap::new(),
27+
}
28+
}
29+
30+
pub fn append_bottom(&mut self, value: I) {
31+
let identifier = value.get_list_identifier();
32+
33+
self.cache_refs.append_bottom(identifier.clone());
34+
self.cache_map.insert(identifier, value);
35+
}
36+
37+
pub fn insert(&mut self, index: AsyncListIndex<I::Identifier>, value: I, is_top: bool, is_bottom: bool) {
38+
let identifier = value.get_list_identifier();
39+
40+
self.cache_map.insert(identifier.clone(), value);
41+
self.cache_refs.insert(index, identifier.clone(), is_top, is_bottom);
42+
}
43+
44+
/// you mut **KNOW** that the item you are inserting is not:
45+
/// - directly next to (Before or After) **any** item in the list
46+
/// - the first or last item in the list
47+
pub fn insert_detached(&mut self, value: I) {
48+
let identifier = value.get_list_identifier();
49+
50+
self.cache_map.insert(identifier.clone(), value);
51+
self.cache_refs.insert_detached(identifier);
52+
}
53+
54+
pub fn bounded_at_top_by(&self) -> Option<I::Identifier> {
55+
self.cache_refs.top_bound()
56+
}
57+
58+
pub fn bounded_at_bottom_by(&self) -> Option<I::Identifier> {
59+
self.cache_refs.bottom_bound()
60+
}
61+
62+
pub fn get(&self, index: AsyncListIndex<I::Identifier>) -> Exists<AsyncListResult<I>> {
63+
let cache_result = self.cache_refs.get(index.clone());
64+
65+
if let Exists::Yes(cache_result) = cache_result {
66+
let content = self.cache_map.get(&cache_result).unwrap().clone();
67+
let is_top = self.cache_refs.top_bound().map(|v| v == content.get_list_identifier()).unwrap_or(false);
68+
let is_bottom = self.cache_refs.bottom_bound().map(|v| v == content.get_list_identifier()).unwrap_or(false);
69+
70+
return Exists::Yes(AsyncListResult { content, is_top, is_bottom });
71+
};
72+
73+
if let Exists::No = cache_result {
74+
return Exists::No;
75+
}
76+
77+
Exists::Unknown
78+
}
79+
80+
pub fn find(&self, identifier: &I::Identifier) -> Option<I> {
81+
self.cache_map.get(identifier).cloned()
82+
}
83+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
use std::{collections::HashMap, fmt::Debug};
2+
3+
use scope_chat::async_list::AsyncListIndex;
4+
5+
use super::refcacheslice::{self, CacheReferencesSlice, Exists};
6+
7+
pub struct CacheReferences<I: Clone + Eq + PartialEq> {
8+
// dense segments are unordered (spooky!) slices of content we do! know about.
9+
// the u64 in the hashmap represents a kind of "segment identifier"
10+
dense_segments: HashMap<u64, CacheReferencesSlice<I>>,
11+
12+
top_bounded_identifier: Option<u64>,
13+
bottom_bounded_identifier: Option<u64>,
14+
}
15+
16+
impl<I: Clone + Eq + PartialEq> Default for CacheReferences<I> {
17+
fn default() -> Self {
18+
Self::new()
19+
}
20+
}
21+
22+
impl<I: Clone + Eq + PartialEq> CacheReferences<I> {
23+
pub fn new() -> Self {
24+
Self {
25+
dense_segments: HashMap::new(),
26+
top_bounded_identifier: None,
27+
bottom_bounded_identifier: None,
28+
}
29+
}
30+
31+
pub fn append_bottom(&mut self, identifier: I) {
32+
let mut id = None;
33+
34+
for (segment_id, segment) in self.dense_segments.iter() {
35+
if let Exists::Yes(_) = segment.get(AsyncListIndex::RelativeToBottom(0)) {
36+
if id.is_some() {
37+
panic!("There should only be one bottom bound segment");
38+
}
39+
40+
id = Some(*segment_id)
41+
}
42+
}
43+
44+
if let Some(id) = id {
45+
self.dense_segments.get_mut(&id).unwrap().append_bottom(identifier);
46+
} else {
47+
self.insert(AsyncListIndex::RelativeToBottom(0), identifier, false, true);
48+
}
49+
}
50+
51+
pub fn top_bound(&self) -> Option<I> {
52+
let index = self.top_bounded_identifier?;
53+
let top_bound = self.dense_segments.get(&index).unwrap();
54+
55+
assert!(top_bound.is_bounded_at_top);
56+
57+
Some(top_bound.item_references.first().unwrap().clone())
58+
}
59+
60+
pub fn bottom_bound(&self) -> Option<I> {
61+
let index = self.bottom_bounded_identifier?;
62+
let bottom_bound = self.dense_segments.get(&index).unwrap();
63+
64+
assert!(bottom_bound.is_bounded_at_bottom);
65+
66+
Some(bottom_bound.item_references.last().unwrap().clone())
67+
}
68+
69+
pub fn get(&self, index: AsyncListIndex<I>) -> Exists<I> {
70+
for segment in self.dense_segments.values() {
71+
let result = segment.get(index.clone());
72+
73+
if let Exists::Yes(value) = result {
74+
return Exists::Yes(value);
75+
} else if let Exists::No = result {
76+
return Exists::No;
77+
}
78+
}
79+
80+
Exists::Unknown
81+
}
82+
83+
/// you mut **KNOW** that the item you are inserting is not:
84+
/// - directly next to (Before or After) **any** item in the list
85+
/// - the first or last item in the list
86+
pub fn insert_detached(&mut self, item: I) {
87+
self.dense_segments.insert(
88+
rand::random(),
89+
CacheReferencesSlice {
90+
is_bounded_at_top: false,
91+
is_bounded_at_bottom: false,
92+
93+
item_references: vec![item],
94+
},
95+
);
96+
}
97+
98+
pub fn insert(&mut self, index: AsyncListIndex<I>, item: I, is_top: bool, is_bottom: bool) {
99+
// insert routine is really complex:
100+
// an insert can "join" together 2 segments
101+
// an insert can append to a segment
102+
// or an insert can construct a new segment
103+
104+
let mut segments = vec![];
105+
106+
for (i, segment) in self.dense_segments.iter() {
107+
if let Some(position) = segment.can_insert(index.clone()) {
108+
segments.push((position, *i));
109+
}
110+
}
111+
112+
if segments.is_empty() {
113+
let id = rand::random();
114+
115+
self.dense_segments.insert(
116+
id,
117+
CacheReferencesSlice {
118+
is_bounded_at_top: is_top,
119+
is_bounded_at_bottom: is_bottom,
120+
121+
item_references: vec![item],
122+
},
123+
);
124+
125+
if is_bottom {
126+
self.bottom_bounded_identifier = Some(id);
127+
}
128+
129+
if is_top {
130+
self.top_bounded_identifier = Some(id);
131+
}
132+
} else if segments.len() == 1 {
133+
self.dense_segments.get_mut(&segments[0].1).unwrap().insert(index.clone(), item, is_bottom, is_top);
134+
135+
if is_top {
136+
self.top_bounded_identifier = Some(segments[0].1)
137+
}
138+
if is_bottom {
139+
self.bottom_bounded_identifier = Some(segments[0].1)
140+
}
141+
} else if segments.len() == 2 {
142+
assert!(!is_top);
143+
assert!(!is_bottom);
144+
145+
let (li, ri) = match (segments[0], segments[1]) {
146+
((refcacheslice::Position::After, lp), (refcacheslice::Position::Before, rp)) => (lp, rp),
147+
((refcacheslice::Position::Before, rp), (refcacheslice::Position::After, lp)) => (lp, rp),
148+
149+
_ => panic!("How are there two candidates that aren't (Before, After) or (After, Before)?"),
150+
};
151+
152+
let (left, right) = if li < ri {
153+
let right = self.dense_segments.remove(&ri).unwrap();
154+
let left = self.dense_segments.remove(&li).unwrap();
155+
156+
(left, right)
157+
} else {
158+
let left = self.dense_segments.remove(&li).unwrap();
159+
let right = self.dense_segments.remove(&ri).unwrap();
160+
161+
(left, right)
162+
};
163+
164+
let mut merged = left.item_references;
165+
166+
merged.push(item);
167+
168+
merged.extend(right.item_references);
169+
170+
let id = rand::random();
171+
172+
self.dense_segments.insert(
173+
id,
174+
CacheReferencesSlice {
175+
is_bounded_at_top: left.is_bounded_at_top,
176+
is_bounded_at_bottom: right.is_bounded_at_bottom,
177+
178+
item_references: merged,
179+
},
180+
);
181+
182+
if left.is_bounded_at_top {
183+
self.top_bounded_identifier = Some(id);
184+
}
185+
186+
if right.is_bounded_at_bottom {
187+
self.bottom_bounded_identifier = Some(id);
188+
}
189+
} else {
190+
panic!("Impossible state")
191+
}
192+
}
193+
}
194+
195+
impl<I: Clone + Eq + PartialEq + Debug> Debug for CacheReferences<I> {
196+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197+
f.debug_struct("CacheReferences")
198+
.field("top_bounded_segment", &self.top_bounded_identifier)
199+
.field("bottom_bounded_segment", &self.bottom_bounded_identifier)
200+
.field("dense_segments", &self.dense_segments)
201+
.finish()
202+
}
203+
}

0 commit comments

Comments
 (0)