inlay_cache.rs

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