inlay_cache.rs

  1use std::{
  2    cmp,
  3    path::{Path, PathBuf},
  4};
  5
  6use crate::{Anchor, ExcerptId};
  7use clock::{Global, Local};
  8use project::InlayHint;
  9use util::post_inc;
 10
 11use collections::{BTreeMap, HashMap};
 12
 13#[derive(Clone, Debug, Default)]
 14pub struct InlayCache {
 15    inlays_per_buffer: HashMap<PathBuf, BufferInlays>,
 16    next_inlay_id: usize,
 17}
 18
 19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 20pub struct AnchorKey {
 21    offset: usize,
 22    version: Local,
 23}
 24
 25#[derive(Clone, Debug)]
 26pub struct OrderedByAnchorOffset<T>(pub BTreeMap<AnchorKey, (Anchor, T)>);
 27
 28impl<T> OrderedByAnchorOffset<T> {
 29    pub fn add(&mut self, anchor: Anchor, t: T) {
 30        let key = AnchorKey {
 31            offset: anchor.text_anchor.offset,
 32            version: anchor.text_anchor.timestamp,
 33        };
 34        self.0.insert(key, (anchor, t));
 35    }
 36
 37    fn into_ordered_elements(self) -> impl Iterator<Item = (Anchor, T)> {
 38        self.0.into_values()
 39    }
 40}
 41
 42impl<T> Default for OrderedByAnchorOffset<T> {
 43    fn default() -> Self {
 44        Self(BTreeMap::default())
 45    }
 46}
 47
 48#[derive(Clone, Debug, Default)]
 49struct BufferInlays {
 50    buffer_version: Global,
 51    inlays_per_excerpts: HashMap<ExcerptId, OrderedByAnchorOffset<(InlayId, InlayHint)>>,
 52}
 53
 54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
 55pub struct InlayId(pub usize);
 56
 57#[derive(Debug)]
 58pub struct InlaysUpdate {
 59    pub to_remove: Vec<InlayId>,
 60    pub to_insert: Vec<(InlayId, Anchor, InlayHint)>,
 61}
 62
 63impl InlayCache {
 64    pub fn inlays_up_to_date(
 65        &self,
 66        buffer_path: &Path,
 67        buffer_version: &Global,
 68        excerpt_id: ExcerptId,
 69    ) -> bool {
 70        let Some(buffer_inlays) = self.inlays_per_buffer.get(buffer_path) else { return false };
 71        let buffer_up_to_date = buffer_version == &buffer_inlays.buffer_version
 72            || buffer_inlays.buffer_version.changed_since(buffer_version);
 73        buffer_up_to_date && buffer_inlays.inlays_per_excerpts.contains_key(&excerpt_id)
 74    }
 75
 76    pub fn update_inlays(
 77        &mut self,
 78        inlay_updates: HashMap<
 79            PathBuf,
 80            (
 81                Global,
 82                HashMap<ExcerptId, Option<OrderedByAnchorOffset<InlayHint>>>,
 83            ),
 84        >,
 85    ) -> InlaysUpdate {
 86        let mut old_inlays = self.inlays_per_buffer.clone();
 87        let mut to_remove = Vec::new();
 88        let mut to_insert = Vec::new();
 89
 90        for (buffer_path, (buffer_version, new_buffer_inlays)) in inlay_updates {
 91            match old_inlays.remove(&buffer_path) {
 92                Some(mut old_buffer_inlays) => {
 93                    for (excerpt_id, new_excerpt_inlays) in new_buffer_inlays {
 94                        if self.inlays_up_to_date(&buffer_path, &buffer_version, excerpt_id) {
 95                            continue;
 96                        }
 97
 98                        let self_inlays_per_buffer = self
 99                            .inlays_per_buffer
100                            .get_mut(&buffer_path)
101                            .expect("element expected: `old_inlays.remove` returned `Some`");
102                        let mut new_excerpt_inlays = match new_excerpt_inlays {
103                            Some(new_inlays) => {
104                                new_inlays.into_ordered_elements().fuse().peekable()
105                            }
106                            None => continue,
107                        };
108                        if old_buffer_inlays
109                            .inlays_per_excerpts
110                            .remove(&excerpt_id)
111                            .is_some()
112                        {
113                            let self_excerpt_inlays = self_inlays_per_buffer
114                                .inlays_per_excerpts
115                                .get_mut(&excerpt_id)
116                                .expect("element expected: `old_excerpt_inlays` is `Some`");
117                            let mut hints_to_add = Vec::<(Anchor, (InlayId, InlayHint))>::new();
118                            // TODO kb update inner buffer_id and version with the new data?
119                            self_excerpt_inlays.0.retain(
120                                |_, (old_anchor, (old_inlay_id, old_inlay))| {
121                                    let mut retain = false;
122
123                                    while let Some(new_offset) = new_excerpt_inlays
124                                        .peek()
125                                        .map(|(new_anchor, _)| new_anchor.text_anchor.offset)
126                                    {
127                                        let old_offset = old_anchor.text_anchor.offset;
128                                        match new_offset.cmp(&old_offset) {
129                                            cmp::Ordering::Less => {
130                                                let (new_anchor, new_inlay) =
131                                                    new_excerpt_inlays.next().expect(
132                                                        "element expected: `peek` returned `Some`",
133                                                    );
134                                                hints_to_add.push((
135                                                    new_anchor,
136                                                    (
137                                                        InlayId(post_inc(&mut self.next_inlay_id)),
138                                                        new_inlay,
139                                                    ),
140                                                ));
141                                            }
142                                            cmp::Ordering::Equal => {
143                                                let (new_anchor, new_inlay) =
144                                                    new_excerpt_inlays.next().expect(
145                                                        "element expected: `peek` returned `Some`",
146                                                    );
147                                                if &new_inlay == old_inlay {
148                                                    retain = true;
149                                                } else {
150                                                    hints_to_add.push((
151                                                        new_anchor,
152                                                        (
153                                                            InlayId(post_inc(
154                                                                &mut self.next_inlay_id,
155                                                            )),
156                                                            new_inlay,
157                                                        ),
158                                                    ));
159                                                }
160                                            }
161                                            cmp::Ordering::Greater => break,
162                                        }
163                                    }
164
165                                    if !retain {
166                                        to_remove.push(*old_inlay_id);
167                                    }
168                                    retain
169                                },
170                            );
171
172                            for (new_anchor, (id, new_inlay)) in hints_to_add {
173                                self_excerpt_inlays.add(new_anchor, (id, new_inlay.clone()));
174                                to_insert.push((id, new_anchor, new_inlay));
175                            }
176                        }
177
178                        for (new_anchor, new_inlay) in new_excerpt_inlays {
179                            let id = InlayId(post_inc(&mut self.next_inlay_id));
180                            self_inlays_per_buffer
181                                .inlays_per_excerpts
182                                .entry(excerpt_id)
183                                .or_default()
184                                .add(new_anchor, (id, new_inlay.clone()));
185                            to_insert.push((id, new_anchor, new_inlay));
186                        }
187                    }
188                }
189                None => {
190                    let mut inlays_per_excerpts: HashMap<
191                        ExcerptId,
192                        OrderedByAnchorOffset<(InlayId, InlayHint)>,
193                    > = HashMap::default();
194                    for (new_excerpt_id, new_ordered_inlays) in new_buffer_inlays {
195                        if let Some(new_ordered_inlays) = new_ordered_inlays {
196                            for (new_anchor, new_inlay) in
197                                new_ordered_inlays.into_ordered_elements()
198                            {
199                                let id = InlayId(post_inc(&mut self.next_inlay_id));
200                                inlays_per_excerpts
201                                    .entry(new_excerpt_id)
202                                    .or_default()
203                                    .add(new_anchor, (id, new_inlay.clone()));
204                                to_insert.push((id, new_anchor, new_inlay));
205                            }
206                        }
207                    }
208                    self.inlays_per_buffer.insert(
209                        buffer_path,
210                        BufferInlays {
211                            buffer_version,
212                            inlays_per_excerpts,
213                        },
214                    );
215                }
216            }
217        }
218
219        for (_, old_buffer_inlays) in old_inlays {
220            for (_, old_excerpt_inlays) in old_buffer_inlays.inlays_per_excerpts {
221                for (_, (id_to_remove, _)) in old_excerpt_inlays.into_ordered_elements() {
222                    to_remove.push(id_to_remove);
223                }
224            }
225        }
226
227        InlaysUpdate {
228            to_remove,
229            to_insert,
230        }
231    }
232}