Skip to main content

editor/editor/
anchors.rs

1//! Anchors: ids on ranges that follow the words through edits.
2
3use super::*;
4
5impl Editor {
6    /// This editor's snapshot of [`crate::Document::anchors`].
7    pub fn anchors(&self) -> &[Anchor] {
8        &self.anchors
9    }
10
11    /// The comment under a point in window coordinates — the space
12    /// [`Self::anchor_bounds`] answers in and the press handler resolves in.
13    ///
14    /// The last match wins, so the newer of two overlapping ranges is the one a
15    /// click opens.
16    pub fn anchor_at(&self, at: gpui::Point<gpui::Pixels>) -> Option<AnchorId> {
17        let (at, _) = self.layouts.hit(at)?;
18        self.anchors
19            .iter()
20            .rfind(|anchor| {
21                let (start, end) = anchor.range.ordered();
22                !anchor.detached() && start <= at && at <= end
23            })
24            .map(|anchor| anchor.id)
25    }
26
27    /// Whether the pointer is over an anchor marked [`Anchor::actionable`].
28    pub(crate) fn over_actionable(&self) -> bool {
29        self.over_anchor.is_some_and(|id| {
30            self.anchors
31                .iter()
32                .any(|anchor| anchor.id == id && anchor.actionable)
33        })
34    }
35
36    /// Where to float a thread, mirroring [`Self::selection_bounds`].
37    pub fn anchor_bounds(&self, id: AnchorId) -> Option<gpui::Bounds<gpui::Pixels>> {
38        let anchor = self.anchors.iter().find(|anchor| anchor.id == id)?;
39        let (point, line_height) = self
40            .layouts
41            .position(anchor.range.ordered().0, Affinity::Downstream)?;
42        Some(gpui::Bounds::new(
43            point,
44            gpui::size(gpui::px(0.0), line_height),
45        ))
46    }
47
48    /// How far down the editor each anchor's first line sits, from the
49    /// editor's own top, in document order. A detached anchor sits where its
50    /// words were.
51    ///
52    /// Read from last frame's layout. An anchor whose line has not been built
53    /// gets the top of its block, guessed until the block is measured; the
54    /// editor repaints when a guess moves.
55    pub fn anchor_tops(&self) -> Vec<(AnchorId, gpui::Pixels)> {
56        let mut tops: Vec<_> = self
57            .anchors
58            .iter()
59            .filter_map(|anchor| {
60                let start = anchor.range.clamp(&self.doc).ordered().0;
61                Some((start, anchor.id, self.layouts.top(start)? - self.origin.y))
62            })
63            .collect();
64        tops.sort_by_key(|(start, ..)| *start);
65        tops.into_iter().map(|(_, id, top)| (id, top)).collect()
66    }
67
68    /// Wash `washes` from the next frame on, over the anchors' own. They are
69    /// positions in [`Self::doc`] as it stands, and are not mapped through
70    /// edits: an app that keeps them current sets them again.
71    pub fn set_washes(&mut self, washes: Vec<(Selection, Annotation)>, cx: &mut Context<Self>) {
72        self.washes = washes;
73        cx.notify();
74    }
75
76    pub fn washes(&self) -> &[(Selection, Annotation)] {
77        &self.washes
78    }
79
80    /// The ranges the renderer washes, clamped because a block can change kind
81    /// under an anchor and take its part with it.
82    pub(super) fn annotations(&self) -> Vec<(Selection, Annotation)> {
83        self.anchors
84            .iter()
85            .filter(|anchor| !anchor.detached())
86            .map(|anchor| (anchor.range, anchor.state))
87            .chain(self.washes.iter().copied())
88            .map(|(range, state)| (range.clamp(&self.doc), state))
89            .collect()
90    }
91}