Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:37:28.784Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

selections_collection.rs

1602 lines · 56.3 KB · rust
1use std::{
2    cmp, fmt, iter, mem,
3    ops::{AddAssign, Deref, DerefMut, Range, Sub},
4    sync::Arc,
5};
6
7use gpui::Pixels;
8use itertools::{Either, Itertools as _};
9use language::{Bias, Point, Selection, SelectionGoal};
10use multi_buffer::{MultiBufferDimension, MultiBufferOffset, ToPoint};
11use util::post_inc;
12
13use crate::{
14    Anchor, DisplayPoint, DisplayRow, MultiBufferSnapshot, SelectMode, ToOffset,
15    display_map::{DisplaySnapshot, ToDisplayPoint},
16    movement::TextLayoutDetails,
17};
18
19#[derive(Debug, Clone)]
20pub struct PendingSelection {
21    selection: Selection<Anchor>,
22    mode: SelectMode,
23}
24
25#[derive(Debug, Clone)]
26pub struct SelectionsCollection {
27    next_selection_id: usize,
28    line_mode: bool,
29    /// The non-pending, non-overlapping selections.
30    /// The [SelectionsCollection::pending] selection could possibly overlap these
31    disjoint: Arc<[Selection<Anchor>]>,
32    /// A pending selection, such as when the mouse is being dragged
33    pending: Option<PendingSelection>,
34    select_mode: SelectMode,
35    is_extending: bool,
36}
37
38impl SelectionsCollection {
39    pub fn new() -> Self {
40        Self {
41            next_selection_id: 1,
42            line_mode: false,
43            disjoint: Arc::default(),
44            pending: Some(PendingSelection {
45                selection: Selection {
46                    id: 0,
47                    start: Anchor::Min,
48                    end: Anchor::Min,
49                    reversed: false,
50                    goal: SelectionGoal::None,
51                },
52                mode: SelectMode::Character,
53            }),
54            select_mode: SelectMode::Character,
55            is_extending: false,
56        }
57    }
58
59    pub fn clone_state(&mut self, other: &SelectionsCollection) {
60        self.next_selection_id = other.next_selection_id;
61        self.line_mode = other.line_mode;
62        self.disjoint = other.disjoint.clone();
63        self.pending.clone_from(&other.pending);
64    }
65
66    pub fn count(&self) -> usize {
67        let mut count = self.disjoint.len();
68        if self.pending.is_some() {
69            count += 1;
70        }
71        count
72    }
73
74    /// The non-pending, non-overlapping selections. There could be a pending selection that
75    /// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a
76    /// pending selection. Returned as selections over Anchors.
77    pub fn disjoint_anchors_arc(&self) -> Arc<[Selection<Anchor>]> {
78        self.disjoint.clone()
79    }
80
81    /// The non-pending, non-overlapping selections. There could be a pending selection that
82    /// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a
83    /// pending selection. Returned as selections over Anchors.
84    pub fn disjoint_anchors(&self) -> &[Selection<Anchor>] {
85        &self.disjoint
86    }
87
88    pub fn disjoint_anchor_ranges(&self) -> impl Iterator<Item = Range<Anchor>> {
89        // Mapping the Arc slice would borrow it, whereas indexing captures it.
90        let disjoint = self.disjoint_anchors_arc();
91        (0..disjoint.len()).map(move |ix| disjoint[ix].range())
92    }
93
94    /// Non-overlapping selections using anchors, including the pending selection.
95    pub fn all_anchors(&self, snapshot: &DisplaySnapshot) -> Arc<[Selection<Anchor>]> {
96        if self.pending.is_none() {
97            self.disjoint_anchors_arc()
98        } else {
99            let all_offset_selections = self.all::<MultiBufferOffset>(snapshot);
100            all_offset_selections
101                .into_iter()
102                .map(|selection| selection_to_anchor_selection(selection, snapshot))
103                .collect()
104        }
105    }
106
107    pub fn pending_anchor(&self) -> Option<&Selection<Anchor>> {
108        self.pending.as_ref().map(|pending| &pending.selection)
109    }
110
111    pub fn pending<D>(&self, snapshot: &DisplaySnapshot) -> Option<Selection<D>>
112    where
113        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
114    {
115        resolve_selections_wrapping_blocks(self.pending_anchor(), &snapshot).next()
116    }
117
118    pub(crate) fn pending_mode(&self) -> Option<SelectMode> {
119        self.pending.as_ref().map(|pending| pending.mode.clone())
120    }
121
122    pub fn all<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<D>>
123    where
124        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
125    {
126        self.all_iter(snapshot).collect()
127    }
128
129    fn all_iter<'a, D>(
130        &'a self,
131        snapshot: &'a DisplaySnapshot,
132    ) -> impl 'a + Iterator<Item = Selection<D>>
133    where
134        D: 'a + MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
135    {
136        let mut disjoint =
137            resolve_selections_wrapping_blocks::<D, _>(self.disjoint.iter(), snapshot).peekable();
138        let mut pending_opt = self.pending::<D>(snapshot);
139        iter::from_fn(move || {
140            if let Some(pending) = pending_opt.as_mut() {
141                while let Some(next_selection) = disjoint.peek() {
142                    if should_merge(
143                        pending.start,
144                        pending.end,
145                        next_selection.start,
146                        next_selection.end,
147                        false,
148                    ) {
149                        let next_selection = disjoint.next().unwrap();
150                        if next_selection.start < pending.start {
151                            pending.start = next_selection.start;
152                        }
153                        if next_selection.end > pending.end {
154                            pending.end = next_selection.end;
155                        }
156                    } else if next_selection.end < pending.start {
157                        return disjoint.next();
158                    } else {
159                        break;
160                    }
161                }
162
163                pending_opt.take()
164            } else {
165                disjoint.next()
166            }
167        })
168    }
169
170    /// Returns all of the selections, adjusted to take into account the selection line_mode
171    pub fn all_adjusted(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<Point>> {
172        let mut selections = self.all::<Point>(&snapshot);
173        if self.line_mode {
174            for selection in &mut selections {
175                let new_range = snapshot.expand_to_line(selection.range());
176                selection.start = new_range.start;
177                selection.end = new_range.end;
178            }
179        }
180        selections
181    }
182
183    /// Returns the newest selection, adjusted to take into account the selection line_mode
184    pub fn newest_adjusted(&self, snapshot: &DisplaySnapshot) -> Selection<Point> {
185        let mut selection = self.newest::<Point>(&snapshot);
186        if self.line_mode {
187            let new_range = snapshot.expand_to_line(selection.range());
188            selection.start = new_range.start;
189            selection.end = new_range.end;
190        }
191        selection
192    }
193
194    pub fn all_adjusted_display(
195        &self,
196        display_map: &DisplaySnapshot,
197    ) -> Vec<Selection<DisplayPoint>> {
198        if self.line_mode {
199            let selections = self.all::<Point>(&display_map);
200            let result = selections
201                .into_iter()
202                .map(|mut selection| {
203                    let new_range = display_map.expand_to_line(selection.range());
204                    selection.start = new_range.start;
205                    selection.end = new_range.end;
206                    selection.map(|point| point.to_display_point(&display_map))
207                })
208                .collect();
209            result
210        } else {
211            self.all_display(display_map)
212        }
213    }
214
215    pub fn disjoint_in_row_range<D>(
216        &self,
217        range: Range<Anchor>,
218        snapshot: &DisplaySnapshot,
219    ) -> Vec<Selection<D>>
220    where
221        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug,
222    {
223        let buffer = snapshot.buffer_snapshot();
224        let start_row = range.start.to_point(buffer).row;
225        let end_row = range.end.to_point(buffer).row;
226        let start_ix = self
227            .disjoint
228            .partition_point(|probe| probe.end.to_point(buffer).row < start_row);
229        let end_ix = self
230            .disjoint
231            .partition_point(|probe| probe.start.to_point(buffer).row <= end_row);
232        resolve_selections_wrapping_blocks(&self.disjoint[start_ix..end_ix], snapshot).collect()
233    }
234
235    pub fn disjoint_in_range<D>(
236        &self,
237        range: Range<Anchor>,
238        snapshot: &DisplaySnapshot,
239    ) -> Vec<Selection<D>>
240    where
241        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug,
242    {
243        let buffer = snapshot.buffer_snapshot();
244        let start_ix = self
245            .disjoint
246            .partition_point(|probe| probe.end.cmp(&range.start, buffer).is_lt());
247        let end_ix = self
248            .disjoint
249            .partition_point(|probe| probe.start.cmp(&range.end, buffer).is_le());
250        resolve_selections_wrapping_blocks(&self.disjoint[start_ix..end_ix], snapshot).collect()
251    }
252
253    pub fn all_display(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<DisplayPoint>> {
254        let disjoint_anchors = &self.disjoint;
255        let mut disjoint =
256            resolve_selections_display(disjoint_anchors.iter(), &snapshot).peekable();
257        let mut pending_opt = resolve_selections_display(self.pending_anchor(), &snapshot).next();
258        iter::from_fn(move || {
259            if let Some(pending) = pending_opt.as_mut() {
260                while let Some(next_selection) = disjoint.peek() {
261                    if should_merge(
262                        pending.start,
263                        pending.end,
264                        next_selection.start,
265                        next_selection.end,
266                        false,
267                    ) {
268                        let next_selection = disjoint.next().unwrap();
269                        if next_selection.start < pending.start {
270                            pending.start = next_selection.start;
271                        }
272                        if next_selection.end > pending.end {
273                            pending.end = next_selection.end;
274                        }
275                    } else if next_selection.end < pending.start {
276                        return disjoint.next();
277                    } else {
278                        break;
279                    }
280                }
281
282                pending_opt.take()
283            } else {
284                disjoint.next()
285            }
286        })
287        .collect()
288    }
289
290    pub fn newest_anchor(&self) -> &Selection<Anchor> {
291        self.pending
292            .as_ref()
293            .map(|s| &s.selection)
294            .or_else(|| self.disjoint.iter().max_by_key(|s| s.id))
295            .unwrap()
296    }
297
298    pub fn newest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
299    where
300        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
301    {
302        resolve_selections_wrapping_blocks([self.newest_anchor()], &snapshot)
303            .next()
304            .unwrap()
305    }
306
307    pub fn newest_display(&self, snapshot: &DisplaySnapshot) -> Selection<DisplayPoint> {
308        resolve_selections_display([self.newest_anchor()], &snapshot)
309            .next()
310            .unwrap()
311    }
312
313    pub fn oldest_anchor(&self) -> &Selection<Anchor> {
314        self.disjoint
315            .iter()
316            .min_by_key(|s| s.id)
317            .or_else(|| self.pending.as_ref().map(|p| &p.selection))
318            .unwrap()
319    }
320
321    pub fn oldest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
322    where
323        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
324    {
325        resolve_selections_wrapping_blocks([self.oldest_anchor()], &snapshot)
326            .next()
327            .unwrap()
328    }
329
330    pub fn first_anchor(&self) -> Selection<Anchor> {
331        self.pending
332            .as_ref()
333            .map(|pending| pending.selection.clone())
334            .unwrap_or_else(|| self.disjoint.first().cloned().unwrap())
335    }
336
337    pub fn first<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
338    where
339        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
340    {
341        // Without a pending selection, the first disjoint selection is also the first resolved
342        // selection, so avoid resolving every selection.
343        if self.pending.is_none()
344            && let Some(first) = self.disjoint.first()
345        {
346            return resolve_selections_wrapping_blocks([first], snapshot)
347                .next()
348                .unwrap();
349        }
350        self.all_iter(snapshot).next().unwrap()
351    }
352
353    pub fn last<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D>
354    where
355        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
356    {
357        if self.pending.is_none()
358            && let Some(last) = self.disjoint.last()
359        {
360            return resolve_selections_wrapping_blocks([last], snapshot)
361                .next()
362                .unwrap();
363        }
364        self.all_iter(snapshot).last().unwrap()
365    }
366
367    /// Returns a list of (potentially backwards!) ranges representing the selections.
368    /// Useful for test assertions, but prefer `.all()` instead.
369    #[cfg(any(test, feature = "test-support"))]
370    pub fn ranges<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Range<D>>
371    where
372        D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
373    {
374        self.all::<D>(snapshot)
375            .iter()
376            .map(|s| {
377                if s.reversed {
378                    s.end..s.start
379                } else {
380                    s.start..s.end
381                }
382            })
383            .collect()
384    }
385
386    #[cfg(any(test, feature = "test-support"))]
387    pub fn display_ranges(&self, display_snapshot: &DisplaySnapshot) -> Vec<Range<DisplayPoint>> {
388        self.disjoint_anchors_arc()
389            .iter()
390            .chain(self.pending_anchor())
391            .map(|s| {
392                if s.reversed {
393                    s.end.to_display_point(display_snapshot)
394                        ..s.start.to_display_point(display_snapshot)
395                } else {
396                    s.start.to_display_point(display_snapshot)
397                        ..s.end.to_display_point(display_snapshot)
398                }
399            })
400            .collect()
401    }
402
403    /// Attempts to build a selection in the provided `DisplayRow` within the
404    /// same range as the provided range of `Pixels`.
405    /// Returns `None` if the range is not empty but it starts past the line's
406    /// length, meaning that the line isn't long enough to be contained within
407    /// part of the provided range.
408    pub fn build_columnar_selection(
409        &mut self,
410        display_map: &DisplaySnapshot,
411        row: DisplayRow,
412        positions: &Range<Pixels>,
413        reversed: bool,
414        text_layout_details: &TextLayoutDetails,
415    ) -> Option<Selection<Point>> {
416        let is_empty = positions.start == positions.end;
417        let line_len = display_map.line_len(row);
418        let line = display_map.layout_row(row, text_layout_details);
419        let start_col = line.closest_index_for_x(positions.start) as u32;
420
421        let (start, end) = if is_empty {
422            let point = DisplayPoint::new(row, std::cmp::min(start_col, line_len));
423            (point, point)
424        } else {
425            if start_col >= line_len {
426                return None;
427            }
428            let start = DisplayPoint::new(row, start_col);
429            let end_col = line.closest_index_for_x(positions.end) as u32;
430            let end = DisplayPoint::new(row, end_col);
431            (start, end)
432        };
433
434        Some(Selection {
435            id: post_inc(&mut self.next_selection_id),
436            start: start.to_point(display_map),
437            end: end.to_point(display_map),
438            reversed,
439            goal: SelectionGoal::HorizontalRange {
440                start: positions.start.into(),
441                end: positions.end.into(),
442            },
443        })
444    }
445
446    /// Attempts to build a selection in the provided buffer row using the
447    /// same UTF-16 column range as specified.
448    /// Returns `None` if the range is not empty but it starts past the line's
449    /// length, meaning that the line isn't long enough to be contained within
450    /// part of the provided range.
451    fn build_columnar_selection_from_tab_expanded_columns(
452        &mut self,
453        display_map: &DisplaySnapshot,
454        buffer_row: u32,
455        positions: &Range<u32>,
456        reversed: bool,
457        text_layout_details: &TextLayoutDetails,
458    ) -> Option<Selection<Point>> {
459        let is_empty = positions.start == positions.end;
460        let line_len = display_map.tab_expanded_line_len(buffer_row);
461
462        let (start, end) = if is_empty {
463            let point = display_map.point_for_tab_expanded_column(buffer_row, positions.start);
464            (point, point)
465        } else {
466            if positions.start >= line_len {
467                return None;
468            }
469
470            let start = display_map.point_for_tab_expanded_column(buffer_row, positions.start);
471            let end = display_map.point_for_tab_expanded_column(buffer_row, positions.end);
472            (start, end)
473        };
474
475        let start_display_point = start.to_display_point(display_map);
476        let end_display_point = end.to_display_point(display_map);
477        let start_x = display_map.x_for_display_point(start_display_point, text_layout_details);
478        let end_x = display_map.x_for_display_point(end_display_point, text_layout_details);
479
480        Some(Selection {
481            id: post_inc(&mut self.next_selection_id),
482            start,
483            end,
484            reversed,
485            goal: SelectionGoal::HorizontalRange {
486                start: start_x.min(end_x).into(),
487                end: start_x.max(end_x).into(),
488            },
489        })
490    }
491
492    /// Finds the next columnar selection by walking display rows one at a time
493    /// so that soft-wrapped lines are considered and not skipped.
494    pub fn find_next_columnar_selection_by_display_row(
495        &mut self,
496        display_map: &DisplaySnapshot,
497        start_row: DisplayRow,
498        end_row: DisplayRow,
499        above: bool,
500        positions: &Range<Pixels>,
501        reversed: bool,
502        text_layout_details: &TextLayoutDetails,
503    ) -> Option<Selection<Point>> {
504        let mut row = start_row;
505        while row != end_row {
506            if above {
507                row.0 -= 1;
508            } else {
509                row.0 += 1;
510            }
511
512            if let Some(selection) = self.build_columnar_selection(
513                display_map,
514                row,
515                positions,
516                reversed,
517                text_layout_details,
518            ) {
519                return Some(selection);
520            }
521        }
522        None
523    }
524
525    /// Finds the next columnar selection by skipping to the next buffer row,
526    /// ignoring soft-wrapped lines.
527    pub fn find_next_columnar_selection_by_buffer_row(
528        &mut self,
529        display_map: &DisplaySnapshot,
530        start_row: DisplayRow,
531        end_row: DisplayRow,
532        above: bool,
533        goal_columns: &Range<u32>,
534        reversed: bool,
535        text_layout_details: &TextLayoutDetails,
536    ) -> Option<Selection<Point>> {
537        let mut row = start_row;
538        let direction = if above { -1 } else { 1 };
539        while row != end_row {
540            let new_row =
541                display_map.start_of_relative_buffer_row(DisplayPoint::new(row, 0), direction);
542            row = new_row.row();
543            let buffer_row = new_row.to_point(display_map).row;
544
545            if let Some(selection) = self.build_columnar_selection_from_tab_expanded_columns(
546                display_map,
547                buffer_row,
548                goal_columns,
549                reversed,
550                text_layout_details,
551            ) {
552                return Some(selection);
553            }
554        }
555        None
556    }
557
558    pub fn change_with<R>(
559        &mut self,
560        snapshot: &DisplaySnapshot,
561        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
562    ) -> (bool, R) {
563        let mut mutable_collection = MutableSelectionsCollection {
564            snapshot,
565            collection: self,
566            selections_changed: false,
567        };
568
569        let result = change(&mut mutable_collection);
570        assert!(
571            !mutable_collection.disjoint.is_empty() || mutable_collection.pending.is_some(),
572            "There must be at least one selection"
573        );
574        if cfg!(debug_assertions) {
575            mutable_collection.disjoint.iter().for_each(|selection| {
576                assert!(
577                     selection.start.cmp(&selection.end, &snapshot).is_le(),
578                    "disjoint selection has start > end: {:?}",
579                    mutable_collection.disjoint
580                );
581                assert!(
582                    snapshot.can_resolve(&selection.start),
583                    "disjoint selection start is not resolvable for the given snapshot:\n{selection:?}",
584                );
585                assert!(
586                    snapshot.can_resolve(&selection.end),
587                    "disjoint selection start is not resolvable for the given snapshot:\n{selection:?}",
588                );
589            });
590            assert!(
591                mutable_collection
592                    .disjoint
593                    .is_sorted_by(|first, second| first.end.cmp(&second.start, &snapshot).is_le()),
594                "disjoint selections are not sorted: {:?}",
595                mutable_collection.disjoint
596            );
597            if let Some(pending) = &mutable_collection.pending {
598                let selection = &pending.selection;
599                assert!(
600                    selection.start.cmp(&selection.end, &snapshot).is_le(),
601                    "pending selection has start > end: {:?}",
602                    selection
603                );
604                assert!(
605                    snapshot.can_resolve(&selection.start),
606                    "pending selection start is not resolvable for the given snapshot: {pending:?}",
607                );
608                assert!(
609                    snapshot.can_resolve(&selection.end),
610                    "pending selection end is not resolvable for the given snapshot: {pending:?}",
611                );
612            }
613        }
614        (mutable_collection.selections_changed, result)
615    }
616
617    pub fn next_selection_id(&self) -> usize {
618        self.next_selection_id
619    }
620
621    pub fn line_mode(&self) -> bool {
622        self.line_mode
623    }
624
625    pub fn set_line_mode(&mut self, line_mode: bool) {
626        self.line_mode = line_mode;
627    }
628
629    pub fn select_mode(&self) -> &SelectMode {
630        &self.select_mode
631    }
632
633    pub fn set_select_mode(&mut self, select_mode: SelectMode) {
634        self.select_mode = select_mode;
635    }
636
637    pub fn is_extending(&self) -> bool {
638        self.is_extending
639    }
640
641    pub fn set_is_extending(&mut self, is_extending: bool) {
642        self.is_extending = is_extending;
643    }
644}
645
646pub struct MutableSelectionsCollection<'snap, 'a> {
647    collection: &'a mut SelectionsCollection,
648    snapshot: &'snap DisplaySnapshot,
649    selections_changed: bool,
650}
651
652impl<'snap, 'a> fmt::Debug for MutableSelectionsCollection<'snap, 'a> {
653    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654        f.debug_struct("MutableSelectionsCollection")
655            .field("collection", &self.collection)
656            .field("selections_changed", &self.selections_changed)
657            .finish()
658    }
659}
660
661impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> {
662    pub fn display_snapshot(&self) -> DisplaySnapshot {
663        self.snapshot.clone()
664    }
665
666    pub fn clear_disjoint(&mut self) {
667        self.collection.disjoint = Arc::default();
668    }
669
670    pub fn delete(&mut self, selection_id: usize) {
671        let mut changed = false;
672        self.collection.disjoint = self
673            .disjoint
674            .iter()
675            .filter(|selection| {
676                let found = selection.id == selection_id;
677                changed |= found;
678                !found
679            })
680            .cloned()
681            .collect();
682
683        self.selections_changed |= changed;
684    }
685
686    pub fn remove_selections_from_buffer(&mut self, buffer_id: language::BufferId) {
687        let mut changed = false;
688
689        let filtered_selections: Arc<[Selection<Anchor>]> = {
690            self.disjoint
691                .iter()
692                .filter(|selection| {
693                    if let Some((selection_buffer_anchor, _)) =
694                        self.snapshot.anchor_to_buffer_anchor(selection.start)
695                    {
696                        let should_remove = selection_buffer_anchor.buffer_id == buffer_id;
697                        changed |= should_remove;
698                        !should_remove
699                    } else {
700                        true
701                    }
702                })
703                .cloned()
704                .collect()
705        };
706
707        if filtered_selections.is_empty() {
708            let buffer_snapshot = self.snapshot.buffer_snapshot();
709            let anchor = buffer_snapshot
710                .excerpts()
711                .find(|excerpt| excerpt.context.start.buffer_id == buffer_id)
712                .and_then(|excerpt| buffer_snapshot.anchor_in_excerpt(excerpt.context.start))
713                .unwrap_or_else(|| self.snapshot.anchor_before(MultiBufferOffset(0)));
714            self.collection.disjoint = Arc::from([Selection {
715                id: post_inc(&mut self.collection.next_selection_id),
716                start: anchor,
717                end: anchor,
718                reversed: false,
719                goal: SelectionGoal::None,
720            }]);
721        } else {
722            self.collection.disjoint = filtered_selections;
723        }
724
725        self.selections_changed |= changed;
726    }
727
728    pub fn clear_pending(&mut self) {
729        if self.collection.pending.is_some() {
730            self.collection.pending = None;
731            self.selections_changed = true;
732        }
733    }
734
735    pub(crate) fn set_pending_anchor_range(&mut self, range: Range<Anchor>, mode: SelectMode) {
736        self.collection.pending = Some(PendingSelection {
737            selection: {
738                let mut start = range.start;
739                let mut end = range.end;
740                let reversed = if start.cmp(&end, self.snapshot).is_gt() {
741                    mem::swap(&mut start, &mut end);
742                    true
743                } else {
744                    false
745                };
746                Selection {
747                    id: post_inc(&mut self.collection.next_selection_id),
748                    start,
749                    end,
750                    reversed,
751                    goal: SelectionGoal::None,
752                }
753            },
754            mode,
755        });
756        self.selections_changed = true;
757    }
758
759    pub(crate) fn set_pending(&mut self, selection: Selection<Anchor>, mode: SelectMode) {
760        self.collection.pending = Some(PendingSelection { selection, mode });
761        self.selections_changed = true;
762    }
763
764    pub fn try_cancel(&mut self) -> bool {
765        if let Some(pending) = self.collection.pending.take() {
766            if self.disjoint.is_empty() {
767                self.collection.disjoint = Arc::from([pending.selection]);
768            }
769            self.selections_changed = true;
770            return true;
771        }
772
773        let mut oldest = self.oldest_anchor().clone();
774        if self.count() > 1 {
775            self.collection.disjoint = Arc::from([oldest]);
776            self.selections_changed = true;
777            return true;
778        }
779
780        if !oldest.start.cmp(&oldest.end, self.snapshot).is_eq() {
781            let head = oldest.head();
782            oldest.start = head;
783            oldest.end = head;
784            self.collection.disjoint = Arc::from([oldest]);
785            self.selections_changed = true;
786            return true;
787        }
788
789        false
790    }
791
792    pub fn insert_range<T>(&mut self, range: Range<T>)
793    where
794        T: ToOffset,
795    {
796        let display_map = self.display_snapshot();
797        let mut selections = self.collection.all(&display_map);
798        let mut start = range.start.to_offset(self.snapshot);
799        let mut end = range.end.to_offset(self.snapshot);
800        let reversed = if start > end {
801            mem::swap(&mut start, &mut end);
802            true
803        } else {
804            false
805        };
806        selections.push(Selection {
807            id: post_inc(&mut self.collection.next_selection_id),
808            start,
809            end,
810            reversed,
811            goal: SelectionGoal::None,
812        });
813        self.select(selections);
814    }
815
816    pub fn select<T>(&mut self, selections: Vec<Selection<T>>)
817    where
818        T: ToOffset + std::marker::Copy + std::fmt::Debug,
819    {
820        let mut selections = selections
821            .into_iter()
822            .map(|selection| selection.map(|it| it.to_offset(self.snapshot)))
823            .map(|mut selection| {
824                if selection.start > selection.end {
825                    mem::swap(&mut selection.start, &mut selection.end);
826                    selection.reversed = true
827                }
828                selection
829            })
830            .collect::<Vec<_>>();
831        selections.sort_unstable_by_key(|s| s.start);
832
833        let mut i = 1;
834        while i < selections.len() {
835            let prev = &selections[i - 1];
836            let current = &selections[i];
837
838            if should_merge(prev.start, prev.end, current.start, current.end, true) {
839                let removed = selections.remove(i);
840                if removed.start < selections[i - 1].start {
841                    selections[i - 1].start = removed.start;
842                }
843                if selections[i - 1].end < removed.end {
844                    selections[i - 1].end = removed.end;
845                }
846            } else {
847                i += 1;
848            }
849        }
850
851        self.collection.disjoint = Arc::from_iter(
852            selections
853                .into_iter()
854                .map(|selection| selection_to_anchor_selection(selection, self.snapshot)),
855        );
856        self.collection.pending = None;
857        self.collection.select_mode = SelectMode::Character;
858        self.selections_changed = true;
859    }
860
861    pub fn select_anchors(&mut self, selections: Vec<Selection<Anchor>>) {
862        let map = self.display_snapshot();
863        let resolved_selections =
864            resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(&selections, &map)
865                .collect::<Vec<_>>();
866        self.select(resolved_selections);
867    }
868
869    pub fn select_ranges<I, T>(&mut self, ranges: I)
870    where
871        I: IntoIterator<Item = Range<T>>,
872        T: ToOffset,
873    {
874        let ranges = ranges
875            .into_iter()
876            .map(|range| range.start.to_offset(self.snapshot)..range.end.to_offset(self.snapshot));
877        self.select_offset_ranges(ranges);
878    }
879
880    fn select_offset_ranges<I>(&mut self, ranges: I)
881    where
882        I: IntoIterator<Item = Range<MultiBufferOffset>>,
883    {
884        let selections = ranges
885            .into_iter()
886            .map(|range| {
887                let mut start = range.start;
888                let mut end = range.end;
889                let reversed = if start > end {
890                    mem::swap(&mut start, &mut end);
891                    true
892                } else {
893                    false
894                };
895                Selection {
896                    id: post_inc(&mut self.collection.next_selection_id),
897                    start,
898                    end,
899                    reversed,
900                    goal: SelectionGoal::None,
901                }
902            })
903            .collect::<Vec<_>>();
904
905        self.select(selections)
906    }
907
908    pub fn select_anchor_ranges<I>(&mut self, ranges: I)
909    where
910        I: IntoIterator<Item = Range<Anchor>>,
911    {
912        let selections = ranges
913            .into_iter()
914            .map(|range| {
915                let mut start = range.start;
916                let mut end = range.end;
917                let reversed = if start.cmp(&end, self.snapshot).is_gt() {
918                    mem::swap(&mut start, &mut end);
919                    true
920                } else {
921                    false
922                };
923                Selection {
924                    id: post_inc(&mut self.collection.next_selection_id),
925                    start,
926                    end,
927                    reversed,
928                    goal: SelectionGoal::None,
929                }
930            })
931            .collect::<Vec<_>>();
932        self.select_anchors(selections)
933    }
934
935    pub fn new_selection_id(&mut self) -> usize {
936        post_inc(&mut self.next_selection_id)
937    }
938
939    pub fn select_display_ranges<T>(&mut self, ranges: T)
940    where
941        T: IntoIterator<Item = Range<DisplayPoint>>,
942    {
943        let selections = ranges
944            .into_iter()
945            .map(|range| {
946                let mut start = range.start;
947                let mut end = range.end;
948                let reversed = if start > end {
949                    mem::swap(&mut start, &mut end);
950                    true
951                } else {
952                    false
953                };
954                Selection {
955                    id: post_inc(&mut self.collection.next_selection_id),
956                    start: start.to_point(self.snapshot),
957                    end: end.to_point(self.snapshot),
958                    reversed,
959                    goal: SelectionGoal::None,
960                }
961            })
962            .collect();
963        self.select(selections);
964    }
965
966    pub fn reverse_selections(&mut self) {
967        let mut new_selections: Vec<Selection<Point>> = Vec::new();
968        let disjoint = self.disjoint.clone();
969        for selection in disjoint
970            .iter()
971            .sorted_by(|first, second| Ord::cmp(&second.id, &first.id))
972        {
973            new_selections.push(Selection {
974                id: self.new_selection_id(),
975                start: selection
976                    .start
977                    .to_display_point(self.snapshot)
978                    .to_point(self.snapshot),
979                end: selection
980                    .end
981                    .to_display_point(self.snapshot)
982                    .to_point(self.snapshot),
983                reversed: selection.reversed,
984                goal: selection.goal,
985            });
986        }
987        self.select(new_selections);
988    }
989
990    pub fn move_with(
991        &mut self,
992        move_selection: &mut dyn FnMut(&DisplaySnapshot, &mut Selection<DisplayPoint>),
993    ) {
994        let mut changed = false;
995        let display_map = self.display_snapshot();
996        let selections = self.collection.all_display(&display_map);
997        let selections = selections
998            .into_iter()
999            .map(|selection| {
1000                let mut moved_selection = selection.clone();
1001                move_selection(&display_map, &mut moved_selection);
1002                if selection != moved_selection {
1003                    changed = true;
1004                }
1005                moved_selection.map(|display_point| display_point.to_point(&display_map))
1006            })
1007            .collect();
1008
1009        if changed {
1010            self.select(selections)
1011        }
1012    }
1013
1014    pub fn move_offsets_with(
1015        &mut self,
1016        move_selection: &mut dyn FnMut(&MultiBufferSnapshot, &mut Selection<MultiBufferOffset>),
1017    ) {
1018        let mut changed = false;
1019        let display_map = self.display_snapshot();
1020        let selections = self
1021            .collection
1022            .all::<MultiBufferOffset>(&display_map)
1023            .into_iter()
1024            .map(|selection| {
1025                let mut moved_selection = selection.clone();
1026                move_selection(self.snapshot, &mut moved_selection);
1027                if selection != moved_selection {
1028                    changed = true;
1029                }
1030                moved_selection
1031            })
1032            .collect();
1033
1034        if changed {
1035            self.select(selections)
1036        }
1037    }
1038
1039    pub fn move_heads_with(
1040        &mut self,
1041        update_head: &mut dyn FnMut(
1042            &DisplaySnapshot,
1043            DisplayPoint,
1044            SelectionGoal,
1045        ) -> (DisplayPoint, SelectionGoal),
1046    ) {
1047        self.move_with(&mut |map, selection| {
1048            let (new_head, new_goal) = update_head(map, selection.head(), selection.goal);
1049            selection.set_head(new_head, new_goal);
1050        });
1051    }
1052
1053    pub fn move_cursors_with(
1054        &mut self,
1055        update_cursor_position: &mut dyn FnMut(
1056            &DisplaySnapshot,
1057            DisplayPoint,
1058            SelectionGoal,
1059        ) -> (DisplayPoint, SelectionGoal),
1060    ) {
1061        self.move_with(&mut |map, selection| {
1062            let (cursor, new_goal) = update_cursor_position(map, selection.head(), selection.goal);
1063            selection.collapse_to(cursor, new_goal)
1064        });
1065    }
1066
1067    pub fn maybe_move_cursors_with(
1068        &mut self,
1069        update_cursor_position: &mut dyn FnMut(
1070            &DisplaySnapshot,
1071            DisplayPoint,
1072            SelectionGoal,
1073        ) -> Option<(DisplayPoint, SelectionGoal)>,
1074    ) {
1075        self.move_cursors_with(&mut |map, point, goal| {
1076            update_cursor_position(map, point, goal).unwrap_or((point, goal))
1077        })
1078    }
1079
1080    pub fn replace_cursors_with(
1081        &mut self,
1082        find_replacement_cursors: impl FnOnce(&DisplaySnapshot) -> Vec<DisplayPoint>,
1083    ) {
1084        let new_selections = find_replacement_cursors(self.snapshot)
1085            .into_iter()
1086            .map(|cursor| {
1087                let cursor_point = cursor.to_point(self.snapshot);
1088                Selection {
1089                    id: post_inc(&mut self.collection.next_selection_id),
1090                    start: cursor_point,
1091                    end: cursor_point,
1092                    reversed: false,
1093                    goal: SelectionGoal::None,
1094                }
1095            })
1096            .collect();
1097        self.select(new_selections);
1098    }
1099
1100    pub fn pending_anchor_mut(&mut self) -> Option<&mut Selection<Anchor>> {
1101        self.selections_changed = true;
1102        self.pending.as_mut().map(|pending| &mut pending.selection)
1103    }
1104}
1105
1106impl Deref for MutableSelectionsCollection<'_, '_> {
1107    type Target = SelectionsCollection;
1108    fn deref(&self) -> &Self::Target {
1109        self.collection
1110    }
1111}
1112
1113impl DerefMut for MutableSelectionsCollection<'_, '_> {
1114    fn deref_mut(&mut self) -> &mut Self::Target {
1115        self.collection
1116    }
1117}
1118
1119fn selection_to_anchor_selection(
1120    selection: Selection<MultiBufferOffset>,
1121    buffer: &MultiBufferSnapshot,
1122) -> Selection<Anchor> {
1123    let end_bias = if selection.start == selection.end {
1124        Bias::Right
1125    } else {
1126        Bias::Left
1127    };
1128    Selection {
1129        id: selection.id,
1130        start: buffer.anchor_after(selection.start),
1131        end: buffer.anchor_at(selection.end, end_bias),
1132        reversed: selection.reversed,
1133        goal: selection.goal,
1134    }
1135}
1136
1137fn resolve_selections_point<'a>(
1138    selections: impl 'a + IntoIterator<Item = &'a Selection<Anchor>>,
1139    map: &'a DisplaySnapshot,
1140) -> impl 'a + Iterator<Item = Selection<Point>> {
1141    let (to_summarize, selections) = selections.into_iter().tee();
1142    let mut summaries = map
1143        .buffer_snapshot()
1144        .summaries_for_anchors::<Point, _>(to_summarize.flat_map(|s| [&s.start, &s.end]))
1145        .into_iter();
1146    selections.map(move |s| {
1147        let start = summaries.next().unwrap();
1148        let end = summaries.next().unwrap();
1149        assert!(
1150            start <= end,
1151            "anchors: start: {:?}, end: {:?}; resolved to: start: {:?}, end: {:?}",
1152            s.start,
1153            s.end,
1154            start,
1155            end
1156        );
1157        Selection {
1158            id: s.id,
1159            start,
1160            end,
1161            reversed: s.reversed,
1162            goal: s.goal,
1163        }
1164    })
1165}
1166
1167/// Panics if passed selections are not in order
1168/// Resolves the anchors to display positions
1169fn resolve_selections_display<'a>(
1170    selections: impl 'a + IntoIterator<Item = &'a Selection<Anchor>>,
1171    map: &'a DisplaySnapshot,
1172) -> impl 'a + Iterator<Item = Selection<DisplayPoint>> {
1173    let selections = resolve_selections_point(selections, map).map(move |s| {
1174        let display_start = map.point_to_display_point(s.start, Bias::Left);
1175        let display_end = map.point_to_display_point(
1176            s.end,
1177            if s.start == s.end {
1178                Bias::Right
1179            } else {
1180                Bias::Left
1181            },
1182        );
1183        assert!(
1184            display_start <= display_end,
1185            "display_start: {:?}, display_end: {:?}",
1186            display_start,
1187            display_end
1188        );
1189        Selection {
1190            id: s.id,
1191            start: display_start,
1192            end: display_end,
1193            reversed: s.reversed,
1194            goal: s.goal,
1195        }
1196    });
1197    coalesce_selections(selections)
1198}
1199
1200/// Resolves the passed in anchors to [`MultiBufferDimension`]s `D`
1201/// wrapping around blocks inbetween.
1202///
1203/// # Panics
1204///
1205/// Panics if passed selections are not in order
1206pub(crate) fn resolve_selections_wrapping_blocks<'a, D, I>(
1207    selections: I,
1208    map: &'a DisplaySnapshot,
1209) -> impl 'a + Iterator<Item = Selection<D>>
1210where
1211    D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
1212    I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
1213{
1214    // Without collapsed content, coalescing in buffer point space is equivalent to coalescing in
1215    // display point space, so skip the per-selection display-coordinate round trip.
1216    if !map.has_collapsed_content() {
1217        Either::Left(resolve_selections_without_display_round_trip(
1218            selections, map,
1219        ))
1220    } else {
1221        Either::Right(resolve_selections_via_display_round_trip(selections, map))
1222    }
1223}
1224
1225fn resolve_selections_without_display_round_trip<'a, D, I>(
1226    selections: I,
1227    map: &'a DisplaySnapshot,
1228) -> impl 'a + Iterator<Item = Selection<D>>
1229where
1230    D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
1231    I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
1232{
1233    let (to_convert, selections) =
1234        coalesce_selections(resolve_selections_point(selections, map)).tee();
1235    let mut converted_endpoints = map
1236        .buffer_snapshot()
1237        .dimensions_from_points::<D>(to_convert.flat_map(|s| [s.start, s.end]));
1238    selections.map(move |s| {
1239        let start = converted_endpoints.next().unwrap();
1240        let end = converted_endpoints.next().unwrap();
1241        Selection {
1242            id: s.id,
1243            start,
1244            end,
1245            reversed: s.reversed,
1246            goal: s.goal,
1247        }
1248    })
1249}
1250
1251fn resolve_selections_via_display_round_trip<'a, D, I>(
1252    selections: I,
1253    map: &'a DisplaySnapshot,
1254) -> impl 'a + Iterator<Item = Selection<D>>
1255where
1256    D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord,
1257    I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
1258{
1259    // Transforms `Anchor -> DisplayPoint -> Point -> DisplayPoint -> D`
1260    // todo(lw): We should be able to short circuit the `Anchor -> DisplayPoint -> Point` to `Anchor -> Point`
1261    let (to_convert, selections) = resolve_selections_display(selections, map).tee();
1262    let mut converted_endpoints =
1263        map.buffer_snapshot()
1264            .dimensions_from_points::<D>(to_convert.flat_map(|s| {
1265                let start = map.display_point_to_point(s.start, Bias::Left);
1266                let end = map.display_point_to_point(s.end, Bias::Right);
1267                assert!(start <= end, "start: {:?}, end: {:?}", start, end);
1268                [start, end]
1269            }));
1270    selections.map(move |s| {
1271        let start = converted_endpoints.next().unwrap();
1272        let end = converted_endpoints.next().unwrap();
1273        assert!(start <= end, "start: {:?}, end: {:?}", start, end);
1274        Selection {
1275            id: s.id,
1276            start,
1277            end,
1278            reversed: s.reversed,
1279            goal: s.goal,
1280        }
1281    })
1282}
1283
1284fn coalesce_selections<D: Ord + fmt::Debug + Copy>(
1285    selections: impl Iterator<Item = Selection<D>>,
1286) -> impl Iterator<Item = Selection<D>> {
1287    let mut selections = selections.peekable();
1288    iter::from_fn(move || {
1289        let mut selection = selections.next()?;
1290        while let Some(next_selection) = selections.peek() {
1291            if should_merge(
1292                selection.start,
1293                selection.end,
1294                next_selection.start,
1295                next_selection.end,
1296                true,
1297            ) {
1298                if selection.reversed == next_selection.reversed {
1299                    selection.end = cmp::max(selection.end, next_selection.end);
1300                    selections.next();
1301                } else {
1302                    selection.end = cmp::max(selection.start, next_selection.start);
1303                    break;
1304                }
1305            } else {
1306                break;
1307            }
1308        }
1309        assert!(
1310            selection.start <= selection.end,
1311            "selection.start: {:?}, selection.end: {:?}, selection.reversed: {:?}",
1312            selection.start,
1313            selection.end,
1314            selection.reversed
1315        );
1316        Some(selection)
1317    })
1318}
1319
1320/// Determines whether two selections should be merged into one.
1321///
1322/// Two selections should be merged when:
1323/// 1. They overlap: the selections share at least one position
1324/// 2. They have the same start position: one contains or equals the other
1325/// 3. A cursor touches a selection boundary: a zero-width selection (cursor) at the
1326///    start or end of another selection should be absorbed into it
1327///
1328/// Note: two selections that merely touch (one ends exactly where the other begins)
1329/// but don't share any positions remain separate, see: https://github.com/zed-industries/zed/issues/24748
1330fn should_merge<T: Ord + Copy>(a_start: T, a_end: T, b_start: T, b_end: T, sorted: bool) -> bool {
1331    let is_overlapping = if sorted {
1332        // When sorted, `a` starts before or at `b`, so overlap means `b` starts before `a` ends
1333        b_start < a_end
1334    } else {
1335        a_start < b_end && b_start < a_end
1336    };
1337
1338    // Selections starting at the same position should always merge (one contains the other)
1339    let same_start = a_start == b_start;
1340
1341    // A cursor (zero-width selection) touching another selection's boundary should merge.
1342    // This handles cases like a cursor at position X merging with a selection that
1343    // starts or ends at X.
1344    let is_cursor_a = a_start == a_end;
1345    let is_cursor_b = b_start == b_end;
1346    let cursor_at_boundary = (is_cursor_a && (a_start == b_start || a_end == b_end))
1347        || (is_cursor_b && (b_start == a_start || b_end == a_end));
1348
1349    is_overlapping || same_start || cursor_at_boundary
1350}
1351
1352#[cfg(test)]
1353mod tests {
1354    use super::*;
1355    use crate::{
1356        MultiBuffer,
1357        display_map::{BlockPlacement, BlockProperties, BlockStyle, DisplayMap, FoldPlaceholder},
1358        test::test_font,
1359    };
1360    use gpui::{AppContext as _, IntoElement as _, div, px};
1361    use project::project_settings::DiagnosticSeverity;
1362    use rand::{Rng as _, rngs::StdRng};
1363    use settings::SettingsStore;
1364    use std::sync::Arc;
1365
1366    fn row_range_snapshot(cx: &mut gpui::TestAppContext, text: &str) -> DisplaySnapshot {
1367        cx.update(|cx| {
1368            let settings = SettingsStore::test(cx);
1369            cx.set_global(settings);
1370            crate::init(cx);
1371        });
1372        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1373        let display_map = cx.new(|cx| {
1374            DisplayMap::new(
1375                buffer,
1376                test_font(),
1377                px(14.),
1378                None,
1379                1,
1380                1,
1381                FoldPlaceholder::test(),
1382                DiagnosticSeverity::Warning,
1383                cx,
1384            )
1385        });
1386        display_map.update(cx, |map, cx| map.snapshot(cx))
1387    }
1388
1389    fn row_range_collection(
1390        offset_ranges: impl IntoIterator<Item = Range<usize>>,
1391        buffer_snapshot: &MultiBufferSnapshot,
1392    ) -> SelectionsCollection {
1393        let selections = offset_ranges
1394            .into_iter()
1395            .enumerate()
1396            .map(|(id, range)| {
1397                selection_to_anchor_selection(
1398                    Selection {
1399                        id,
1400                        start: MultiBufferOffset(range.start),
1401                        end: MultiBufferOffset(range.end),
1402                        reversed: false,
1403                        goal: SelectionGoal::None,
1404                    },
1405                    buffer_snapshot,
1406                )
1407            })
1408            .collect::<Vec<_>>();
1409        let mut collection = SelectionsCollection::new();
1410        collection.disjoint = Arc::from(selections);
1411        collection.pending = None;
1412        collection
1413    }
1414
1415    /// `disjoint_in_row_range` selects by whole rows, so a selection sharing a queried row must be
1416    /// returned even when its columns don't overlap the queried range. `disjoint_in_range` compares
1417    /// exact offsets and would miss this case.
1418    #[gpui::test]
1419    fn disjoint_in_row_range_matches_whole_row(cx: &mut gpui::TestAppContext) {
1420        let snapshot = row_range_snapshot(cx, "aaaa\nbbbbbbbb\ncccc");
1421        let buffer_snapshot = snapshot.buffer_snapshot();
1422
1423        // A selection on row 1 spanning columns 3..5 (buffer offsets 8..10).
1424        let collection = row_range_collection([8..10], buffer_snapshot);
1425
1426        // Query row 1 at columns 0..1, entirely before the selection's columns.
1427        let range = buffer_snapshot.anchor_before(MultiBufferOffset(5))
1428            ..buffer_snapshot.anchor_before(MultiBufferOffset(6));
1429        let result = collection.disjoint_in_row_range::<Point>(range, &snapshot);
1430
1431        assert_eq!(
1432            result.len(),
1433            1,
1434            "row range should include the selection sharing the queried row"
1435        );
1436        assert_eq!(result[0].start, Point::new(1, 3));
1437        assert_eq!(result[0].end, Point::new(1, 5));
1438    }
1439
1440    /// A selection on a row outside the queried row range must not be returned. This guards the
1441    /// row-overlap boundary against becoming over-inclusive.
1442    #[gpui::test]
1443    fn disjoint_in_row_range_excludes_other_rows(cx: &mut gpui::TestAppContext) {
1444        let snapshot = row_range_snapshot(cx, "aaaa\nbbbbbbbb\ncccc");
1445        let buffer_snapshot = snapshot.buffer_snapshot();
1446
1447        // A selection on row 2 (buffer offsets 14..16).
1448        let collection = row_range_collection([14..16], buffer_snapshot);
1449
1450        // Query only row 0, two rows away from the selection.
1451        let range = buffer_snapshot.anchor_before(MultiBufferOffset(0))
1452            ..buffer_snapshot.anchor_before(MultiBufferOffset(1));
1453        let result = collection.disjoint_in_row_range::<Point>(range, &snapshot);
1454
1455        assert!(
1456            result.is_empty(),
1457            "row range should exclude a selection on a non-queried row"
1458        );
1459    }
1460
1461    /// A selection spanning multiple rows must be returned when the query touches any of those rows,
1462    /// not just when the query shares the selection's start or end row.
1463    #[gpui::test]
1464    fn disjoint_in_row_range_matches_interior_row(cx: &mut gpui::TestAppContext) {
1465        let snapshot = row_range_snapshot(cx, "aaaa\nbbbb\ncccc\ndddd");
1466        let buffer_snapshot = snapshot.buffer_snapshot();
1467
1468        // A selection spanning row 1 column 1 through row 3 column 2 (buffer offsets 6..17).
1469        let collection = row_range_collection([6..17], buffer_snapshot);
1470
1471        // Query only row 2, an interior row of the selection.
1472        let range = buffer_snapshot.anchor_before(MultiBufferOffset(11))
1473            ..buffer_snapshot.anchor_before(MultiBufferOffset(12));
1474        let result = collection.disjoint_in_row_range::<Point>(range, &snapshot);
1475
1476        assert_eq!(
1477            result.len(),
1478            1,
1479            "row range should include a selection whose interior row is queried"
1480        );
1481        assert_eq!(result[0].start, Point::new(1, 1));
1482        assert_eq!(result[0].end, Point::new(3, 2));
1483    }
1484
1485    #[gpui::test(iterations = 20)]
1486    fn fast_and_slow_selection_resolution_match_without_collapsed_content(
1487        cx: &mut gpui::TestAppContext,
1488        mut rng: StdRng,
1489    ) {
1490        cx.update(|cx| {
1491            let settings = SettingsStore::test(cx);
1492            cx.set_global(settings);
1493            crate::init(cx);
1494        });
1495
1496        let text = random_text(&mut rng);
1497        let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
1498        let display_map = cx.new(|cx| {
1499            DisplayMap::new(
1500                buffer.clone(),
1501                test_font(),
1502                px(14.),
1503                Some(px(rng.random_range(80.0..=180.0))),
1504                1,
1505                1,
1506                FoldPlaceholder::test(),
1507                DiagnosticSeverity::Warning,
1508                cx,
1509            )
1510        });
1511
1512        let snapshot = display_map.update(cx, |map, cx| {
1513            let buffer_snapshot = buffer.read(cx).snapshot(cx);
1514            map.insert_blocks(
1515                [BlockProperties {
1516                    placement: BlockPlacement::Above(
1517                        buffer_snapshot.anchor_after(MultiBufferOffset(text.len() / 2)),
1518                    ),
1519                    height: Some(2),
1520                    style: BlockStyle::Fixed,
1521                    render: Arc::new(|_| div().into_any_element()),
1522                    priority: 0,
1523                }],
1524                cx,
1525            );
1526            map.snapshot(cx)
1527        });
1528        assert!(!snapshot.has_collapsed_content());
1529
1530        let selections = random_anchor_selections(&snapshot, text.len(), &mut rng);
1531
1532        let fast_points =
1533            resolve_selections_without_display_round_trip::<Point, _>(selections.iter(), &snapshot)
1534                .collect::<Vec<_>>();
1535        let slow_points =
1536            resolve_selections_via_display_round_trip::<Point, _>(selections.iter(), &snapshot)
1537                .collect::<Vec<_>>();
1538        assert_eq!(fast_points, slow_points);
1539
1540        let fast_offsets = resolve_selections_without_display_round_trip::<MultiBufferOffset, _>(
1541            selections.iter(),
1542            &snapshot,
1543        )
1544        .collect::<Vec<_>>();
1545        let slow_offsets = resolve_selections_via_display_round_trip::<MultiBufferOffset, _>(
1546            selections.iter(),
1547            &snapshot,
1548        )
1549        .collect::<Vec<_>>();
1550        assert_eq!(fast_offsets, slow_offsets);
1551    }
1552
1553    fn random_text(rng: &mut StdRng) -> String {
1554        let mut text = String::new();
1555        for line in 0..rng.random_range(8..32) {
1556            if line > 0 {
1557                text.push('\n');
1558            }
1559            for _ in 0..rng.random_range(8..48) {
1560                let ch = match rng.random_range(0..12) {
1561                    0 => '\t',
1562                    1 => ' ',
1563                    _ => rng.random_range(b'a'..=b'z') as char,
1564                };
1565                text.push(ch);
1566            }
1567        }
1568        text
1569    }
1570
1571    fn random_anchor_selections(
1572        snapshot: &DisplaySnapshot,
1573        text_len: usize,
1574        rng: &mut StdRng,
1575    ) -> Vec<Selection<Anchor>> {
1576        let buffer = snapshot.buffer_snapshot();
1577        let mut selections = Vec::new();
1578        let mut offset = 0;
1579        for id in 0..rng.random_range(1..32) {
1580            if offset > text_len {
1581                break;
1582            }
1583
1584            let start = rng.random_range(offset..=text_len);
1585            let end = rng.random_range(start..=text_len);
1586            selections.push(Selection {
1587                id,
1588                start: MultiBufferOffset(start),
1589                end: MultiBufferOffset(end),
1590                reversed: rng.random(),
1591                goal: SelectionGoal::None,
1592            });
1593            offset = end.saturating_add(rng.random_range(0..=4));
1594        }
1595
1596        selections
1597            .into_iter()
1598            .map(|selection| selection_to_anchor_selection(selection, buffer))
1599            .collect()
1600    }
1601}
1602
Served at tenant.openagents/omega Member data and write actions are omitted.