editor/editor/anchors.rs
1//! Anchors: ids on ranges that follow the words through edits.
2
3use super::*;
4
5impl Editor {
6 /// The comment ranges, mapped up to date with the document.
7 ///
8 /// A range that reads [`Anchor::detached`] lost the words it pointed at.
9 /// It is kept rather than dropped, because whether that means "outdated" or
10 /// "resolved" is the app's question.
11 pub fn anchors(&self) -> &[Anchor] {
12 &self.anchors
13 }
14
15 /// Hand over the whole list — the app keeps the threads, this keeps their
16 /// ranges. One entry point rather than add/remove/update, since the app is
17 /// already holding the list that decides all three.
18 pub fn set_anchors(&mut self, anchors: Vec<Anchor>, cx: &mut Context<Self>) {
19 self.anchors = anchors;
20 cx.notify();
21 }
22
23 /// The comment under a point in window coordinates — the space
24 /// [`Self::anchor_bounds`] answers in and the press handler resolves in.
25 ///
26 /// The last match wins, so the newer of two overlapping ranges is the one a
27 /// click opens.
28 pub fn anchor_at(&self, at: gpui::Point<gpui::Pixels>) -> Option<AnchorId> {
29 let at = self.layouts.hit(at)?;
30 self.anchors
31 .iter()
32 .rfind(|anchor| {
33 let (start, end) = anchor.range.ordered();
34 !anchor.detached() && start <= at && at <= end
35 })
36 .map(|anchor| anchor.id)
37 }
38
39 /// Where to float a thread, mirroring [`Self::selection_bounds`].
40 pub fn anchor_bounds(&self, id: AnchorId) -> Option<gpui::Bounds<gpui::Pixels>> {
41 let anchor = self.anchors.iter().find(|anchor| anchor.id == id)?;
42 let (point, line_height) = self.layouts.position(anchor.range.ordered().0)?;
43 Some(gpui::Bounds::new(
44 point,
45 gpui::size(gpui::px(0.0), line_height),
46 ))
47 }
48
49 /// The ranges the renderer washes, clamped because a block can change kind
50 /// under an anchor and take its part with it.
51 pub(super) fn annotations(&self) -> Vec<(Selection, Annotation)> {
52 self.anchors
53 .iter()
54 .filter(|anchor| !anchor.detached())
55 .map(|anchor| (anchor.range.clamp(&self.doc), anchor.state))
56 .collect()
57 }
58}