use std::{ cmp, fmt, iter, mem, ops::{AddAssign, Deref, DerefMut, Range, Sub}, sync::Arc,}; use gpui::Pixels;use itertools::{Either, Itertools as _};use language::{Bias, Point, Selection, SelectionGoal};use multi_buffer::{MultiBufferDimension, MultiBufferOffset, ToPoint};use util::post_inc; use crate::{ Anchor, DisplayPoint, DisplayRow, MultiBufferSnapshot, SelectMode, ToOffset, display_map::{DisplaySnapshot, ToDisplayPoint}, movement::TextLayoutDetails,}; #[derive(Debug, Clone)]pub struct PendingSelection { selection: Selection<Anchor>, mode: SelectMode,} #[derive(Debug, Clone)]pub struct SelectionsCollection { next_selection_id: usize, line_mode: bool, /// The non-pending, non-overlapping selections. /// The [SelectionsCollection::pending] selection could possibly overlap these disjoint: Arc<[Selection<Anchor>]>, /// A pending selection, such as when the mouse is being dragged pending: Option<PendingSelection>, select_mode: SelectMode, is_extending: bool,} impl SelectionsCollection { pub fn new() -> Self { Self { next_selection_id: 1, line_mode: false, disjoint: Arc::default(), pending: Some(PendingSelection { selection: Selection { id: 0, start: Anchor::Min, end: Anchor::Min, reversed: false, goal: SelectionGoal::None, }, mode: SelectMode::Character, }), select_mode: SelectMode::Character, is_extending: false, } } pub fn clone_state(&mut self, other: &SelectionsCollection) { self.next_selection_id = other.next_selection_id; self.line_mode = other.line_mode; self.disjoint = other.disjoint.clone(); self.pending.clone_from(&other.pending); } pub fn count(&self) -> usize { let mut count = self.disjoint.len(); if self.pending.is_some() { count += 1; } count } /// The non-pending, non-overlapping selections. There could be a pending selection that /// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a /// pending selection. Returned as selections over Anchors. pub fn disjoint_anchors_arc(&self) -> Arc<[Selection<Anchor>]> { self.disjoint.clone() } /// The non-pending, non-overlapping selections. There could be a pending selection that /// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a /// pending selection. Returned as selections over Anchors. pub fn disjoint_anchors(&self) -> &[Selection<Anchor>] { &self.disjoint } pub fn disjoint_anchor_ranges(&self) -> impl Iterator<Item = Range<Anchor>> { // Mapping the Arc slice would borrow it, whereas indexing captures it. let disjoint = self.disjoint_anchors_arc(); (0..disjoint.len()).map(move |ix| disjoint[ix].range()) } /// Non-overlapping selections using anchors, including the pending selection. pub fn all_anchors(&self, snapshot: &DisplaySnapshot) -> Arc<[Selection<Anchor>]> { if self.pending.is_none() { self.disjoint_anchors_arc() } else { let all_offset_selections = self.all::<MultiBufferOffset>(snapshot); all_offset_selections .into_iter() .map(|selection| selection_to_anchor_selection(selection, snapshot)) .collect() } } pub fn pending_anchor(&self) -> Option<&Selection<Anchor>> { self.pending.as_ref().map(|pending| &pending.selection) } pub fn pending<D>(&self, snapshot: &DisplaySnapshot) -> Option<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { resolve_selections_wrapping_blocks(self.pending_anchor(), &snapshot).next() } pub(crate) fn pending_mode(&self) -> Option<SelectMode> { self.pending.as_ref().map(|pending| pending.mode.clone()) } pub fn all<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { self.all_iter(snapshot).collect() } fn all_iter<'a, D>( &'a self, snapshot: &'a DisplaySnapshot, ) -> impl 'a + Iterator<Item = Selection<D>> where D: 'a + MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { let mut disjoint = resolve_selections_wrapping_blocks::<D, _>(self.disjoint.iter(), snapshot).peekable(); let mut pending_opt = self.pending::<D>(snapshot); iter::from_fn(move || { if let Some(pending) = pending_opt.as_mut() { while let Some(next_selection) = disjoint.peek() { if should_merge( pending.start, pending.end, next_selection.start, next_selection.end, false, ) { let next_selection = disjoint.next().unwrap(); if next_selection.start < pending.start { pending.start = next_selection.start; } if next_selection.end > pending.end { pending.end = next_selection.end; } } else if next_selection.end < pending.start { return disjoint.next(); } else { break; } } pending_opt.take() } else { disjoint.next() } }) } /// Returns all of the selections, adjusted to take into account the selection line_mode pub fn all_adjusted(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<Point>> { let mut selections = self.all::<Point>(&snapshot); if self.line_mode { for selection in &mut selections { let new_range = snapshot.expand_to_line(selection.range()); selection.start = new_range.start; selection.end = new_range.end; } } selections } /// Returns the newest selection, adjusted to take into account the selection line_mode pub fn newest_adjusted(&self, snapshot: &DisplaySnapshot) -> Selection<Point> { let mut selection = self.newest::<Point>(&snapshot); if self.line_mode { let new_range = snapshot.expand_to_line(selection.range()); selection.start = new_range.start; selection.end = new_range.end; } selection } pub fn all_adjusted_display( &self, display_map: &DisplaySnapshot, ) -> Vec<Selection<DisplayPoint>> { if self.line_mode { let selections = self.all::<Point>(&display_map); let result = selections .into_iter() .map(|mut selection| { let new_range = display_map.expand_to_line(selection.range()); selection.start = new_range.start; selection.end = new_range.end; selection.map(|point| point.to_display_point(&display_map)) }) .collect(); result } else { self.all_display(display_map) } } pub fn disjoint_in_row_range<D>( &self, range: Range<Anchor>, snapshot: &DisplaySnapshot, ) -> Vec<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug, { let buffer = snapshot.buffer_snapshot(); let start_row = range.start.to_point(buffer).row; let end_row = range.end.to_point(buffer).row; let start_ix = self .disjoint .partition_point(|probe| probe.end.to_point(buffer).row < start_row); let end_ix = self .disjoint .partition_point(|probe| probe.start.to_point(buffer).row <= end_row); resolve_selections_wrapping_blocks(&self.disjoint[start_ix..end_ix], snapshot).collect() } pub fn disjoint_in_range<D>( &self, range: Range<Anchor>, snapshot: &DisplaySnapshot, ) -> Vec<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug, { let buffer = snapshot.buffer_snapshot(); let start_ix = self .disjoint .partition_point(|probe| probe.end.cmp(&range.start, buffer).is_lt()); let end_ix = self .disjoint .partition_point(|probe| probe.start.cmp(&range.end, buffer).is_le()); resolve_selections_wrapping_blocks(&self.disjoint[start_ix..end_ix], snapshot).collect() } pub fn all_display(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<DisplayPoint>> { let disjoint_anchors = &self.disjoint; let mut disjoint = resolve_selections_display(disjoint_anchors.iter(), &snapshot).peekable(); let mut pending_opt = resolve_selections_display(self.pending_anchor(), &snapshot).next(); iter::from_fn(move || { if let Some(pending) = pending_opt.as_mut() { while let Some(next_selection) = disjoint.peek() { if should_merge( pending.start, pending.end, next_selection.start, next_selection.end, false, ) { let next_selection = disjoint.next().unwrap(); if next_selection.start < pending.start { pending.start = next_selection.start; } if next_selection.end > pending.end { pending.end = next_selection.end; } } else if next_selection.end < pending.start { return disjoint.next(); } else { break; } } pending_opt.take() } else { disjoint.next() } }) .collect() } pub fn newest_anchor(&self) -> &Selection<Anchor> { self.pending .as_ref() .map(|s| &s.selection) .or_else(|| self.disjoint.iter().max_by_key(|s| s.id)) .unwrap() } pub fn newest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { resolve_selections_wrapping_blocks([self.newest_anchor()], &snapshot) .next() .unwrap() } pub fn newest_display(&self, snapshot: &DisplaySnapshot) -> Selection<DisplayPoint> { resolve_selections_display([self.newest_anchor()], &snapshot) .next() .unwrap() } pub fn oldest_anchor(&self) -> &Selection<Anchor> { self.disjoint .iter() .min_by_key(|s| s.id) .or_else(|| self.pending.as_ref().map(|p| &p.selection)) .unwrap() } pub fn oldest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { resolve_selections_wrapping_blocks([self.oldest_anchor()], &snapshot) .next() .unwrap() } pub fn first_anchor(&self) -> Selection<Anchor> { self.pending .as_ref() .map(|pending| pending.selection.clone()) .unwrap_or_else(|| self.disjoint.first().cloned().unwrap()) } pub fn first<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { // Without a pending selection, the first disjoint selection is also the first resolved // selection, so avoid resolving every selection. if self.pending.is_none() && let Some(first) = self.disjoint.first() { return resolve_selections_wrapping_blocks([first], snapshot) .next() .unwrap(); } self.all_iter(snapshot).next().unwrap() } pub fn last<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { if self.pending.is_none() && let Some(last) = self.disjoint.last() { return resolve_selections_wrapping_blocks([last], snapshot) .next() .unwrap(); } self.all_iter(snapshot).last().unwrap() } /// Returns a list of (potentially backwards!) ranges representing the selections. /// Useful for test assertions, but prefer `.all()` instead. #[cfg(any(test, feature = "test-support"))] pub fn ranges<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Range<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { self.all::<D>(snapshot) .iter() .map(|s| { if s.reversed { s.end..s.start } else { s.start..s.end } }) .collect() } #[cfg(any(test, feature = "test-support"))] pub fn display_ranges(&self, display_snapshot: &DisplaySnapshot) -> Vec<Range<DisplayPoint>> { self.disjoint_anchors_arc() .iter() .chain(self.pending_anchor()) .map(|s| { if s.reversed { s.end.to_display_point(display_snapshot) ..s.start.to_display_point(display_snapshot) } else { s.start.to_display_point(display_snapshot) ..s.end.to_display_point(display_snapshot) } }) .collect() } /// Attempts to build a selection in the provided `DisplayRow` within the /// same range as the provided range of `Pixels`. /// Returns `None` if the range is not empty but it starts past the line's /// length, meaning that the line isn't long enough to be contained within /// part of the provided range. pub fn build_columnar_selection( &mut self, display_map: &DisplaySnapshot, row: DisplayRow, positions: &Range<Pixels>, reversed: bool, text_layout_details: &TextLayoutDetails, ) -> Option<Selection<Point>> { let is_empty = positions.start == positions.end; let line_len = display_map.line_len(row); let line = display_map.layout_row(row, text_layout_details); let start_col = line.closest_index_for_x(positions.start) as u32; let (start, end) = if is_empty { let point = DisplayPoint::new(row, std::cmp::min(start_col, line_len)); (point, point) } else { if start_col >= line_len { return None; } let start = DisplayPoint::new(row, start_col); let end_col = line.closest_index_for_x(positions.end) as u32; let end = DisplayPoint::new(row, end_col); (start, end) }; Some(Selection { id: post_inc(&mut self.next_selection_id), start: start.to_point(display_map), end: end.to_point(display_map), reversed, goal: SelectionGoal::HorizontalRange { start: positions.start.into(), end: positions.end.into(), }, }) } /// Attempts to build a selection in the provided buffer row using the /// same UTF-16 column range as specified. /// Returns `None` if the range is not empty but it starts past the line's /// length, meaning that the line isn't long enough to be contained within /// part of the provided range. fn build_columnar_selection_from_tab_expanded_columns( &mut self, display_map: &DisplaySnapshot, buffer_row: u32, positions: &Range<u32>, reversed: bool, text_layout_details: &TextLayoutDetails, ) -> Option<Selection<Point>> { let is_empty = positions.start == positions.end; let line_len = display_map.tab_expanded_line_len(buffer_row); let (start, end) = if is_empty { let point = display_map.point_for_tab_expanded_column(buffer_row, positions.start); (point, point) } else { if positions.start >= line_len { return None; } let start = display_map.point_for_tab_expanded_column(buffer_row, positions.start); let end = display_map.point_for_tab_expanded_column(buffer_row, positions.end); (start, end) }; let start_display_point = start.to_display_point(display_map); let end_display_point = end.to_display_point(display_map); let start_x = display_map.x_for_display_point(start_display_point, text_layout_details); let end_x = display_map.x_for_display_point(end_display_point, text_layout_details); Some(Selection { id: post_inc(&mut self.next_selection_id), start, end, reversed, goal: SelectionGoal::HorizontalRange { start: start_x.min(end_x).into(), end: start_x.max(end_x).into(), }, }) } /// Finds the next columnar selection by walking display rows one at a time /// so that soft-wrapped lines are considered and not skipped. pub fn find_next_columnar_selection_by_display_row( &mut self, display_map: &DisplaySnapshot, start_row: DisplayRow, end_row: DisplayRow, above: bool, positions: &Range<Pixels>,Lines 1–500 of 1601. Load more
use std::{ cmp, fmt, iter, mem, ops::{AddAssign, Deref, DerefMut, Range, Sub}, sync::Arc, }; use gpui::Pixels; use itertools::{Either, Itertools as _}; use language::{Bias, Point, Selection, SelectionGoal}; use multi_buffer::{MultiBufferDimension, MultiBufferOffset, ToPoint}; use util::post_inc; use crate::{ Anchor, DisplayPoint, DisplayRow, MultiBufferSnapshot, SelectMode, ToOffset, display_map::{DisplaySnapshot, ToDisplayPoint}, movement::TextLayoutDetails, }; #[derive(Debug, Clone)] pub struct PendingSelection { selection: Selection<Anchor>, mode: SelectMode, } #[derive(Debug, Clone)] pub struct SelectionsCollection { next_selection_id: usize, line_mode: bool, /// The non-pending, non-overlapping selections. /// The [SelectionsCollection::pending] selection could possibly overlap these disjoint: Arc<[Selection<Anchor>]>, /// A pending selection, such as when the mouse is being dragged pending: Option<PendingSelection>, select_mode: SelectMode, is_extending: bool, } impl SelectionsCollection { pub fn new() -> Self { Self { next_selection_id: 1, line_mode: false, disjoint: Arc::default(), pending: Some(PendingSelection { selection: Selection { id: 0, start: Anchor::Min, end: Anchor::Min, reversed: false, goal: SelectionGoal::None, }, mode: SelectMode::Character, }), select_mode: SelectMode::Character, is_extending: false, } } pub fn clone_state(&mut self, other: &SelectionsCollection) { self.next_selection_id = other.next_selection_id; self.line_mode = other.line_mode; self.disjoint = other.disjoint.clone(); self.pending.clone_from(&other.pending); } pub fn count(&self) -> usize { let mut count = self.disjoint.len(); if self.pending.is_some() { count += 1; } count } /// The non-pending, non-overlapping selections. There could be a pending selection that /// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a /// pending selection. Returned as selections over Anchors. pub fn disjoint_anchors_arc(&self) -> Arc<[Selection<Anchor>]> { self.disjoint.clone() } /// The non-pending, non-overlapping selections. There could be a pending selection that /// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a /// pending selection. Returned as selections over Anchors. pub fn disjoint_anchors(&self) -> &[Selection<Anchor>] { &self.disjoint } pub fn disjoint_anchor_ranges(&self) -> impl Iterator<Item = Range<Anchor>> { // Mapping the Arc slice would borrow it, whereas indexing captures it. let disjoint = self.disjoint_anchors_arc(); (0..disjoint.len()).map(move |ix| disjoint[ix].range()) } /// Non-overlapping selections using anchors, including the pending selection. pub fn all_anchors(&self, snapshot: &DisplaySnapshot) -> Arc<[Selection<Anchor>]> { if self.pending.is_none() { self.disjoint_anchors_arc() } else { let all_offset_selections = self.all::<MultiBufferOffset>(snapshot); all_offset_selections .into_iter() .map(|selection| selection_to_anchor_selection(selection, snapshot)) .collect() } } pub fn pending_anchor(&self) -> Option<&Selection<Anchor>> { self.pending.as_ref().map(|pending| &pending.selection) } pub fn pending<D>(&self, snapshot: &DisplaySnapshot) -> Option<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { resolve_selections_wrapping_blocks(self.pending_anchor(), &snapshot).next() } pub(crate) fn pending_mode(&self) -> Option<SelectMode> { self.pending.as_ref().map(|pending| pending.mode.clone()) } pub fn all<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { self.all_iter(snapshot).collect() } fn all_iter<'a, D>( &'a self, snapshot: &'a DisplaySnapshot, ) -> impl 'a + Iterator<Item = Selection<D>> where D: 'a + MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { let mut disjoint = resolve_selections_wrapping_blocks::<D, _>(self.disjoint.iter(), snapshot).peekable(); let mut pending_opt = self.pending::<D>(snapshot); iter::from_fn(move || { if let Some(pending) = pending_opt.as_mut() { while let Some(next_selection) = disjoint.peek() { if should_merge( pending.start, pending.end, next_selection.start, next_selection.end, false, ) { let next_selection = disjoint.next().unwrap(); if next_selection.start < pending.start { pending.start = next_selection.start; } if next_selection.end > pending.end { pending.end = next_selection.end; } } else if next_selection.end < pending.start { return disjoint.next(); } else { break; } } pending_opt.take() } else { disjoint.next() } }) } /// Returns all of the selections, adjusted to take into account the selection line_mode pub fn all_adjusted(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<Point>> { let mut selections = self.all::<Point>(&snapshot); if self.line_mode { for selection in &mut selections { let new_range = snapshot.expand_to_line(selection.range()); selection.start = new_range.start; selection.end = new_range.end; } } selections } /// Returns the newest selection, adjusted to take into account the selection line_mode pub fn newest_adjusted(&self, snapshot: &DisplaySnapshot) -> Selection<Point> { let mut selection = self.newest::<Point>(&snapshot); if self.line_mode { let new_range = snapshot.expand_to_line(selection.range()); selection.start = new_range.start; selection.end = new_range.end; } selection } pub fn all_adjusted_display( &self, display_map: &DisplaySnapshot, ) -> Vec<Selection<DisplayPoint>> { if self.line_mode { let selections = self.all::<Point>(&display_map); let result = selections .into_iter() .map(|mut selection| { let new_range = display_map.expand_to_line(selection.range()); selection.start = new_range.start; selection.end = new_range.end; selection.map(|point| point.to_display_point(&display_map)) }) .collect(); result } else { self.all_display(display_map) } } pub fn disjoint_in_row_range<D>( &self, range: Range<Anchor>, snapshot: &DisplaySnapshot, ) -> Vec<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug, { let buffer = snapshot.buffer_snapshot(); let start_row = range.start.to_point(buffer).row; let end_row = range.end.to_point(buffer).row; let start_ix = self .disjoint .partition_point(|probe| probe.end.to_point(buffer).row < start_row); let end_ix = self .disjoint .partition_point(|probe| probe.start.to_point(buffer).row <= end_row); resolve_selections_wrapping_blocks(&self.disjoint[start_ix..end_ix], snapshot).collect() } pub fn disjoint_in_range<D>( &self, range: Range<Anchor>, snapshot: &DisplaySnapshot, ) -> Vec<Selection<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord + std::fmt::Debug, { let buffer = snapshot.buffer_snapshot(); let start_ix = self .disjoint .partition_point(|probe| probe.end.cmp(&range.start, buffer).is_lt()); let end_ix = self .disjoint .partition_point(|probe| probe.start.cmp(&range.end, buffer).is_le()); resolve_selections_wrapping_blocks(&self.disjoint[start_ix..end_ix], snapshot).collect() } pub fn all_display(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<DisplayPoint>> { let disjoint_anchors = &self.disjoint; let mut disjoint = resolve_selections_display(disjoint_anchors.iter(), &snapshot).peekable(); let mut pending_opt = resolve_selections_display(self.pending_anchor(), &snapshot).next(); iter::from_fn(move || { if let Some(pending) = pending_opt.as_mut() { while let Some(next_selection) = disjoint.peek() { if should_merge( pending.start, pending.end, next_selection.start, next_selection.end, false, ) { let next_selection = disjoint.next().unwrap(); if next_selection.start < pending.start { pending.start = next_selection.start; } if next_selection.end > pending.end { pending.end = next_selection.end; } } else if next_selection.end < pending.start { return disjoint.next(); } else { break; } } pending_opt.take() } else { disjoint.next() } }) .collect() } pub fn newest_anchor(&self) -> &Selection<Anchor> { self.pending .as_ref() .map(|s| &s.selection) .or_else(|| self.disjoint.iter().max_by_key(|s| s.id)) .unwrap() } pub fn newest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { resolve_selections_wrapping_blocks([self.newest_anchor()], &snapshot) .next() .unwrap() } pub fn newest_display(&self, snapshot: &DisplaySnapshot) -> Selection<DisplayPoint> { resolve_selections_display([self.newest_anchor()], &snapshot) .next() .unwrap() } pub fn oldest_anchor(&self) -> &Selection<Anchor> { self.disjoint .iter() .min_by_key(|s| s.id) .or_else(|| self.pending.as_ref().map(|p| &p.selection)) .unwrap() } pub fn oldest<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { resolve_selections_wrapping_blocks([self.oldest_anchor()], &snapshot) .next() .unwrap() } pub fn first_anchor(&self) -> Selection<Anchor> { self.pending .as_ref() .map(|pending| pending.selection.clone()) .unwrap_or_else(|| self.disjoint.first().cloned().unwrap()) } pub fn first<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { // Without a pending selection, the first disjoint selection is also the first resolved // selection, so avoid resolving every selection. if self.pending.is_none() && let Some(first) = self.disjoint.first() { return resolve_selections_wrapping_blocks([first], snapshot) .next() .unwrap(); } self.all_iter(snapshot).next().unwrap() } pub fn last<D>(&self, snapshot: &DisplaySnapshot) -> Selection<D> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { if self.pending.is_none() && let Some(last) = self.disjoint.last() { return resolve_selections_wrapping_blocks([last], snapshot) .next() .unwrap(); } self.all_iter(snapshot).last().unwrap() } /// Returns a list of (potentially backwards!) ranges representing the selections. /// Useful for test assertions, but prefer `.all()` instead. #[cfg(any(test, feature = "test-support"))] pub fn ranges<D>(&self, snapshot: &DisplaySnapshot) -> Vec<Range<D>> where D: MultiBufferDimension + Sub + AddAssign<<D as Sub>::Output> + Ord, { self.all::<D>(snapshot) .iter() .map(|s| { if s.reversed { s.end..s.start } else { s.start..s.end } }) .collect() } #[cfg(any(test, feature = "test-support"))] pub fn display_ranges(&self, display_snapshot: &DisplaySnapshot) -> Vec<Range<DisplayPoint>> { self.disjoint_anchors_arc() .iter() .chain(self.pending_anchor()) .map(|s| { if s.reversed { s.end.to_display_point(display_snapshot) ..s.start.to_display_point(display_snapshot) } else { s.start.to_display_point(display_snapshot) ..s.end.to_display_point(display_snapshot) } }) .collect() } /// Attempts to build a selection in the provided `DisplayRow` within the /// same range as the provided range of `Pixels`. /// Returns `None` if the range is not empty but it starts past the line's /// length, meaning that the line isn't long enough to be contained within /// part of the provided range. pub fn build_columnar_selection( &mut self, display_map: &DisplaySnapshot, row: DisplayRow, positions: &Range<Pixels>, reversed: bool, text_layout_details: &TextLayoutDetails, ) -> Option<Selection<Point>> { let is_empty = positions.start == positions.end; let line_len = display_map.line_len(row); let line = display_map.layout_row(row, text_layout_details); let start_col = line.closest_index_for_x(positions.start) as u32; let (start, end) = if is_empty { let point = DisplayPoint::new(row, std::cmp::min(start_col, line_len)); (point, point) } else { if start_col >= line_len { return None; } let start = DisplayPoint::new(row, start_col); let end_col = line.closest_index_for_x(positions.end) as u32; let end = DisplayPoint::new(row, end_col); (start, end) }; Some(Selection { id: post_inc(&mut self.next_selection_id), start: start.to_point(display_map), end: end.to_point(display_map), reversed, goal: SelectionGoal::HorizontalRange { start: positions.start.into(), end: positions.end.into(), }, }) } /// Attempts to build a selection in the provided buffer row using the /// same UTF-16 column range as specified. /// Returns `None` if the range is not empty but it starts past the line's /// length, meaning that the line isn't long enough to be contained within /// part of the provided range. fn build_columnar_selection_from_tab_expanded_columns( &mut self, display_map: &DisplaySnapshot, buffer_row: u32, positions: &Range<u32>, reversed: bool, text_layout_details: &TextLayoutDetails, ) -> Option<Selection<Point>> { let is_empty = positions.start == positions.end; let line_len = display_map.tab_expanded_line_len(buffer_row); let (start, end) = if is_empty { let point = display_map.point_for_tab_expanded_column(buffer_row, positions.start); (point, point) } else { if positions.start >= line_len { return None; } let start = display_map.point_for_tab_expanded_column(buffer_row, positions.start); let end = display_map.point_for_tab_expanded_column(buffer_row, positions.end); (start, end) }; let start_display_point = start.to_display_point(display_map); let end_display_point = end.to_display_point(display_map); let start_x = display_map.x_for_display_point(start_display_point, text_layout_details); let end_x = display_map.x_for_display_point(end_display_point, text_layout_details); Some(Selection { id: post_inc(&mut self.next_selection_id), start, end, reversed, goal: SelectionGoal::HorizontalRange { start: start_x.min(end_x).into(), end: start_x.max(end_x).into(), }, }) } /// Finds the next columnar selection by walking display rows one at a time /// so that soft-wrapped lines are considered and not skipped. pub fn find_next_columnar_selection_by_display_row( &mut self, display_map: &DisplaySnapshot, start_row: DisplayRow, end_row: DisplayRow, above: bool, positions: &Range<Pixels>,