use super::*; impl Editor { pub fn sync_selections( &mut self, other: Entity<Editor>, cx: &mut Context<Self>, ) -> gpui::Subscription { let other_selections = other.read(cx).selections.disjoint_anchors().to_vec(); if !other_selections.is_empty() { self.selections .change_with(&self.display_snapshot(cx), |selections| { selections.select_anchors(other_selections); }); } let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| { if let EditorEvent::SelectionsChanged { local: true } = other_evt { let other_selections = other.read(cx).selections.disjoint_anchors().to_vec(); if other_selections.is_empty() { return; } let snapshot = this.display_snapshot(cx); this.selections.change_with(&snapshot, |selections| { selections.select_anchors(other_selections); }); } }); let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| { if let EditorEvent::SelectionsChanged { local: true } = this_evt { let these_selections = this.selections.disjoint_anchors().to_vec(); if these_selections.is_empty() { return; } other.update(cx, |other_editor, cx| { let snapshot = other_editor.display_snapshot(cx); other_editor .selections .change_with(&snapshot, |selections| { selections.select_anchors(these_selections); }) }); } }); Subscription::join(other_subscription, this_subscription) } /// Changes selections using the provided mutation function. Changes to `self.selections` occur /// immediately, but when run within `transact` or `with_selection_effects_deferred` other /// effects of selection change occur at the end of the transaction. pub fn change_selections<R>( &mut self, effects: SelectionEffects, window: &mut Window, cx: &mut Context<Self>, change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R, ) -> R { let snapshot = self.display_snapshot(cx); if let Some(state) = &mut self.deferred_selection_effects_state { state.effects.scroll = effects.scroll.or(state.effects.scroll); state.effects.completions = effects.completions; state.effects.nav_history = effects.nav_history.or(state.effects.nav_history); let (changed, result) = self.selections.change_with(&snapshot, change); state.changed |= changed; return result; } let mut state = DeferredSelectionEffectsState { changed: false, effects, old_cursor_position: self.selections.newest_anchor().head(), history_entry: SelectionHistoryEntry { selections: self.selections.disjoint_anchors_arc(), select_next_state: self.select_next_state.clone(), select_prev_state: self.select_prev_state.clone(), add_selections_state: self.add_selections_state.clone(), }, }; let (changed, result) = self.selections.change_with(&snapshot, change); state.changed = state.changed || changed; if self.defer_selection_effects { self.deferred_selection_effects_state = Some(state); } else { self.apply_selection_effects(state, window, cx); } result } /// Defers the effects of selection change, so that the effects of multiple calls to /// `change_selections` are applied at the end. This way these intermediate states aren't added /// to selection history and the state of popovers based on selection position aren't /// erroneously updated. pub fn with_selection_effects_deferred<R>( &mut self, window: &mut Window, cx: &mut Context<Self>, update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R, ) -> R { let already_deferred = self.defer_selection_effects; self.defer_selection_effects = true; let result = update(self, window, cx); if !already_deferred { self.defer_selection_effects = false; if let Some(state) = self.deferred_selection_effects_state.take() { self.apply_selection_effects(state, window, cx); } } result } pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool { self.selections .all_adjusted(snapshot) .iter() .any(|selection| !selection.is_empty()) } pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool { if self .selections .pending_anchor() .is_some_and(|pending_selection| { let snapshot = self.buffer().read(cx).snapshot(cx); pending_selection.range().includes(range, &snapshot) }) { return true; } self.selections .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx)) .into_iter() .any(|selection| { // This is needed to cover a corner case, if we just check for an existing // selection in the fold range, having a cursor at the start of the fold // marks it as selected. Non-empty selections don't cause this. let length = selection.end - selection.start; length > 0 }) } pub fn has_pending_nonempty_selection(&self) -> bool { let pending_nonempty_selection = match self.selections.pending_anchor() { Some(Selection { start, end, .. }) => start != end, None => false, }; pending_nonempty_selection || (self.columnar_selection_state.is_some() && self.selections.disjoint_anchors().len() > 1) } pub fn has_pending_selection(&self) -> bool { self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some() } pub fn set_selections_from_remote( &mut self, selections: Vec<Selection<Anchor>>, pending_selection: Option<Selection<Anchor>>, window: &mut Window, cx: &mut Context<Self>, ) { let old_cursor_position = self.selections.newest_anchor().head(); self.selections .change_with(&self.display_snapshot(cx), |s| { s.select_anchors(selections); if let Some(pending_selection) = pending_selection { s.set_pending(pending_selection, SelectMode::Character); } else { s.clear_pending(); } }); self.selections_did_change( false, &old_cursor_position, SelectionEffects::default(), window, cx, ); } pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) { if self.selection_mark_mode { self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.move_with(&mut |_, sel| { sel.collapse_to(sel.head(), SelectionGoal::None); }); }) } self.selection_mark_mode = true; cx.notify(); } pub fn swap_selection_ends( &mut self, _: &actions::SwapSelectionEnds, window: &mut Window, cx: &mut Context<Self>, ) { self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.move_with(&mut |_, sel| { if sel.start != sel.end { sel.reversed = !sel.reversed } }); }); self.request_autoscroll(Autoscroll::newest(), cx); cx.notify(); } pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) { let buffer = self.buffer.read(cx).snapshot(cx); let mut selection = self .selections .first::<MultiBufferOffset>(&self.display_snapshot(cx)); selection.set_head(buffer.len(), SelectionGoal::None); self.change_selections(Default::default(), window, cx, |s| { s.select(vec![selection]); }); } pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) { self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges(vec![Anchor::Min..Anchor::Max]); }); } pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut selections = self.selections.all::<Point>(&display_map); let max_point = display_map.buffer_snapshot().max_point(); for selection in &mut selections { let rows = selection.spanned_rows(true, &display_map); selection.start = Point::new(rows.start.0, 0); selection.end = cmp::min(max_point, Point::new(rows.end.0, 0)); selection.reversed = false; } self.change_selections(Default::default(), window, cx, |s| { s.select(selections); }); } pub fn split_selection_into_lines( &mut self, action: &SplitSelectionIntoLines, window: &mut Window, cx: &mut Context<Self>, ) { let selections = self .selections .all::<Point>(&self.display_snapshot(cx)) .into_iter() .map(|selection| selection.start..selection.end) .collect::<Vec<_>>(); self.unfold_ranges(&selections, true, false, cx); let mut new_selection_ranges = Vec::new(); { let buffer = self.buffer.read(cx).read(cx); for selection in selections { for row in selection.start.row..selection.end.row { let line_start = Point::new(row, 0); let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row))); if action.keep_selections { // Keep the selection range for each line let selection_start = if row == selection.start.row { selection.start } else { line_start }; new_selection_ranges.push(selection_start..line_end); } else { // Collapse to cursor at end of line new_selection_ranges.push(line_end..line_end); } } let is_multiline_selection = selection.start.row != selection.end.row; // Don't insert last one if it's a multi-line selection ending at the start of a line, // so this action feels more ergonomic when paired with other selection operations let should_skip_last = is_multiline_selection && selection.end.column == 0; if !should_skip_last { if action.keep_selections { if is_multiline_selection { let line_start = Point::new(selection.end.row, 0); new_selection_ranges.push(line_start..selection.end); } else { new_selection_ranges.push(selection.start..selection.end); } } else { new_selection_ranges.push(selection.end..selection.end); } } } } self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges(new_selection_ranges); }); } pub fn add_selection_above( &mut self, action: &AddSelectionAbove, window: &mut Window, cx: &mut Context<Self>, ) { self.add_selection(true, action.skip_soft_wrap, window, cx); } pub fn add_selection_below( &mut self, action: &AddSelectionBelow, window: &mut Window, cx: &mut Context<Self>, ) { self.add_selection(false, action.skip_soft_wrap, window, cx); } pub fn select_all_matches( &mut self, _action: &SelectAllMatches, window: &mut Window, cx: &mut Context<Self>, ) -> Result<()> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); self.select_next_match_internal(&display_map, false, None, window, cx)?; let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done) else { return Ok(()); }; let mut new_selections = Vec::new(); let initial_selection = self.selections.oldest::<MultiBufferOffset>(&display_map); let reversed = initial_selection.reversed; let buffer = display_map.buffer_snapshot(); let query_matches = select_next_state .query .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len())); for query_match in query_matches.into_iter() { let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O let offset_range = if reversed { MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start()) } else { MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end()) }; let is_partial_word_match = select_next_state.wordwise && (buffer.is_inside_word(offset_range.start, None) || buffer.is_inside_word(offset_range.end, None)); let is_initial_selection = MultiBufferOffset(query_match.start()) == initial_selection.start && MultiBufferOffset(query_match.end()) == initial_selection.end; if !is_partial_word_match && !is_initial_selection { new_selections.push(offset_range); } } // Ensure that the initial range is the last selection, as // `MutableSelectionsCollection::select_ranges` makes the last selection // the newest selection, which the editor then relies on as the primary // cursor for scroll targeting. Without this, the last match would then // be automatically focused when the user started editing the selected // matches. let initial_directed_range = if reversed { initial_selection.end..initial_selection.start } else { initial_selection.start..initial_selection.end }; new_selections.push(initial_directed_range); select_next_state.done = true; self.unfold_ranges(&new_selections, false, false, cx); self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { selections.select_ranges(new_selections) }); Ok(()) } pub fn select_next( &mut self, action: &SelectNext, window: &mut Window, cx: &mut Context<Self>, ) -> Result<()> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); self.select_next_match_internal( &display_map, action.replace_newest, Some(Autoscroll::newest()), window, cx, ) } pub fn select_previous( &mut self, action: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>, ) -> Result<()> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = display_map.buffer_snapshot(); let mut selections = self.selections.all::<MultiBufferOffset>(&display_map); if let Some(mut select_prev_state) = self.select_prev_state.take() { let query = &select_prev_state.query; if !select_prev_state.done { let first_selection = selections .iter() .min_by_key(|s| s.id) .context("missing selection for select previous action")?; let last_selection = selections .iter() .max_by_key(|s| s.id) .context("missing selection for select previous action")?; let mut next_selected_range = None; // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer. let bytes_before_last_selection = buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start); let bytes_after_first_selection = buffer.reversed_bytes_in_range(first_selection.end..buffer.len()); let query_matches = query .stream_find_iter(bytes_before_last_selection) .map(|result| (last_selection.start, result)) .chain( query .stream_find_iter(bytes_after_first_selection) .map(|result| (buffer.len(), result)), ); for (end_offset, query_match) in query_matches { let query_match = query_match.context("query match for select previous action")?; let offset_range = end_offset - query_match.end()..end_offset - query_match.start(); if !select_prev_state.wordwise || (!buffer.is_inside_word(offset_range.start, None) && !buffer.is_inside_word(offset_range.end, None)) { next_selected_range = Some(offset_range); break; } } if let Some(next_selected_range) = next_selected_range { self.select_match_ranges( next_selected_range, last_selection.reversed, action.replace_newest, Some(Autoscroll::newest()), window, cx, ); } else { select_prev_state.done = true; } } self.select_prev_state = Some(select_prev_state); } else { let mut only_carets = true; let mut same_text_selected = true; let mut selected_text = None; let mut selections_iter = selections.iter().peekable(); while let Some(selection) = selections_iter.next() { if selection.start != selection.end { only_carets = false; } if same_text_selected { if selected_text.is_none() { selected_text = Some(buffer.text_for_range(selection.range()).collect::<String>()); } if let Some(next_selection) = selections_iter.peek() { if next_selection.len() == selection.len() { let next_selected_text = buffer .text_for_range(next_selection.range()) .collect::<String>(); if Some(next_selected_text) != selected_text { same_text_selected = false; selected_text = None; } } else { same_text_selected = false; selected_text = None; } } } } Lines 1–500 of 2404. Load more
use super::*; impl Editor { pub fn sync_selections( &mut self, other: Entity<Editor>, cx: &mut Context<Self>, ) -> gpui::Subscription { let other_selections = other.read(cx).selections.disjoint_anchors().to_vec(); if !other_selections.is_empty() { self.selections .change_with(&self.display_snapshot(cx), |selections| { selections.select_anchors(other_selections); }); } let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| { if let EditorEvent::SelectionsChanged { local: true } = other_evt { let other_selections = other.read(cx).selections.disjoint_anchors().to_vec(); if other_selections.is_empty() { return; } let snapshot = this.display_snapshot(cx); this.selections.change_with(&snapshot, |selections| { selections.select_anchors(other_selections); }); } }); let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| { if let EditorEvent::SelectionsChanged { local: true } = this_evt { let these_selections = this.selections.disjoint_anchors().to_vec(); if these_selections.is_empty() { return; } other.update(cx, |other_editor, cx| { let snapshot = other_editor.display_snapshot(cx); other_editor .selections .change_with(&snapshot, |selections| { selections.select_anchors(these_selections); }) }); } }); Subscription::join(other_subscription, this_subscription) } /// Changes selections using the provided mutation function. Changes to `self.selections` occur /// immediately, but when run within `transact` or `with_selection_effects_deferred` other /// effects of selection change occur at the end of the transaction. pub fn change_selections<R>( &mut self, effects: SelectionEffects, window: &mut Window, cx: &mut Context<Self>, change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R, ) -> R { let snapshot = self.display_snapshot(cx); if let Some(state) = &mut self.deferred_selection_effects_state { state.effects.scroll = effects.scroll.or(state.effects.scroll); state.effects.completions = effects.completions; state.effects.nav_history = effects.nav_history.or(state.effects.nav_history); let (changed, result) = self.selections.change_with(&snapshot, change); state.changed |= changed; return result; } let mut state = DeferredSelectionEffectsState { changed: false, effects, old_cursor_position: self.selections.newest_anchor().head(), history_entry: SelectionHistoryEntry { selections: self.selections.disjoint_anchors_arc(), select_next_state: self.select_next_state.clone(), select_prev_state: self.select_prev_state.clone(), add_selections_state: self.add_selections_state.clone(), }, }; let (changed, result) = self.selections.change_with(&snapshot, change); state.changed = state.changed || changed; if self.defer_selection_effects { self.deferred_selection_effects_state = Some(state); } else { self.apply_selection_effects(state, window, cx); } result } /// Defers the effects of selection change, so that the effects of multiple calls to /// `change_selections` are applied at the end. This way these intermediate states aren't added /// to selection history and the state of popovers based on selection position aren't /// erroneously updated. pub fn with_selection_effects_deferred<R>( &mut self, window: &mut Window, cx: &mut Context<Self>, update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R, ) -> R { let already_deferred = self.defer_selection_effects; self.defer_selection_effects = true; let result = update(self, window, cx); if !already_deferred { self.defer_selection_effects = false; if let Some(state) = self.deferred_selection_effects_state.take() { self.apply_selection_effects(state, window, cx); } } result } pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool { self.selections .all_adjusted(snapshot) .iter() .any(|selection| !selection.is_empty()) } pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool { if self .selections .pending_anchor() .is_some_and(|pending_selection| { let snapshot = self.buffer().read(cx).snapshot(cx); pending_selection.range().includes(range, &snapshot) }) { return true; } self.selections .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx)) .into_iter() .any(|selection| { // This is needed to cover a corner case, if we just check for an existing // selection in the fold range, having a cursor at the start of the fold // marks it as selected. Non-empty selections don't cause this. let length = selection.end - selection.start; length > 0 }) } pub fn has_pending_nonempty_selection(&self) -> bool { let pending_nonempty_selection = match self.selections.pending_anchor() { Some(Selection { start, end, .. }) => start != end, None => false, }; pending_nonempty_selection || (self.columnar_selection_state.is_some() && self.selections.disjoint_anchors().len() > 1) } pub fn has_pending_selection(&self) -> bool { self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some() } pub fn set_selections_from_remote( &mut self, selections: Vec<Selection<Anchor>>, pending_selection: Option<Selection<Anchor>>, window: &mut Window, cx: &mut Context<Self>, ) { let old_cursor_position = self.selections.newest_anchor().head(); self.selections .change_with(&self.display_snapshot(cx), |s| { s.select_anchors(selections); if let Some(pending_selection) = pending_selection { s.set_pending(pending_selection, SelectMode::Character); } else { s.clear_pending(); } }); self.selections_did_change( false, &old_cursor_position, SelectionEffects::default(), window, cx, ); } pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) { if self.selection_mark_mode { self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.move_with(&mut |_, sel| { sel.collapse_to(sel.head(), SelectionGoal::None); }); }) } self.selection_mark_mode = true; cx.notify(); } pub fn swap_selection_ends( &mut self, _: &actions::SwapSelectionEnds, window: &mut Window, cx: &mut Context<Self>, ) { self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.move_with(&mut |_, sel| { if sel.start != sel.end { sel.reversed = !sel.reversed } }); }); self.request_autoscroll(Autoscroll::newest(), cx); cx.notify(); } pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) { let buffer = self.buffer.read(cx).snapshot(cx); let mut selection = self .selections .first::<MultiBufferOffset>(&self.display_snapshot(cx)); selection.set_head(buffer.len(), SelectionGoal::None); self.change_selections(Default::default(), window, cx, |s| { s.select(vec![selection]); }); } pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) { self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges(vec![Anchor::Min..Anchor::Max]); }); } pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut selections = self.selections.all::<Point>(&display_map); let max_point = display_map.buffer_snapshot().max_point(); for selection in &mut selections { let rows = selection.spanned_rows(true, &display_map); selection.start = Point::new(rows.start.0, 0); selection.end = cmp::min(max_point, Point::new(rows.end.0, 0)); selection.reversed = false; } self.change_selections(Default::default(), window, cx, |s| { s.select(selections); }); } pub fn split_selection_into_lines( &mut self, action: &SplitSelectionIntoLines, window: &mut Window, cx: &mut Context<Self>, ) { let selections = self .selections .all::<Point>(&self.display_snapshot(cx)) .into_iter() .map(|selection| selection.start..selection.end) .collect::<Vec<_>>(); self.unfold_ranges(&selections, true, false, cx); let mut new_selection_ranges = Vec::new(); { let buffer = self.buffer.read(cx).read(cx); for selection in selections { for row in selection.start.row..selection.end.row { let line_start = Point::new(row, 0); let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row))); if action.keep_selections { // Keep the selection range for each line let selection_start = if row == selection.start.row { selection.start } else { line_start }; new_selection_ranges.push(selection_start..line_end); } else { // Collapse to cursor at end of line new_selection_ranges.push(line_end..line_end); } } let is_multiline_selection = selection.start.row != selection.end.row; // Don't insert last one if it's a multi-line selection ending at the start of a line, // so this action feels more ergonomic when paired with other selection operations let should_skip_last = is_multiline_selection && selection.end.column == 0; if !should_skip_last { if action.keep_selections { if is_multiline_selection { let line_start = Point::new(selection.end.row, 0); new_selection_ranges.push(line_start..selection.end); } else { new_selection_ranges.push(selection.start..selection.end); } } else { new_selection_ranges.push(selection.end..selection.end); } } } } self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges(new_selection_ranges); }); } pub fn add_selection_above( &mut self, action: &AddSelectionAbove, window: &mut Window, cx: &mut Context<Self>, ) { self.add_selection(true, action.skip_soft_wrap, window, cx); } pub fn add_selection_below( &mut self, action: &AddSelectionBelow, window: &mut Window, cx: &mut Context<Self>, ) { self.add_selection(false, action.skip_soft_wrap, window, cx); } pub fn select_all_matches( &mut self, _action: &SelectAllMatches, window: &mut Window, cx: &mut Context<Self>, ) -> Result<()> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); self.select_next_match_internal(&display_map, false, None, window, cx)?; let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done) else { return Ok(()); }; let mut new_selections = Vec::new(); let initial_selection = self.selections.oldest::<MultiBufferOffset>(&display_map); let reversed = initial_selection.reversed; let buffer = display_map.buffer_snapshot(); let query_matches = select_next_state .query .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len())); for query_match in query_matches.into_iter() { let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O let offset_range = if reversed { MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start()) } else { MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end()) }; let is_partial_word_match = select_next_state.wordwise && (buffer.is_inside_word(offset_range.start, None) || buffer.is_inside_word(offset_range.end, None)); let is_initial_selection = MultiBufferOffset(query_match.start()) == initial_selection.start && MultiBufferOffset(query_match.end()) == initial_selection.end; if !is_partial_word_match && !is_initial_selection { new_selections.push(offset_range); } } // Ensure that the initial range is the last selection, as // `MutableSelectionsCollection::select_ranges` makes the last selection // the newest selection, which the editor then relies on as the primary // cursor for scroll targeting. Without this, the last match would then // be automatically focused when the user started editing the selected // matches. let initial_directed_range = if reversed { initial_selection.end..initial_selection.start } else { initial_selection.start..initial_selection.end }; new_selections.push(initial_directed_range); select_next_state.done = true; self.unfold_ranges(&new_selections, false, false, cx); self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { selections.select_ranges(new_selections) }); Ok(()) } pub fn select_next( &mut self, action: &SelectNext, window: &mut Window, cx: &mut Context<Self>, ) -> Result<()> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); self.select_next_match_internal( &display_map, action.replace_newest, Some(Autoscroll::newest()), window, cx, ) } pub fn select_previous( &mut self, action: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>, ) -> Result<()> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = display_map.buffer_snapshot(); let mut selections = self.selections.all::<MultiBufferOffset>(&display_map); if let Some(mut select_prev_state) = self.select_prev_state.take() { let query = &select_prev_state.query; if !select_prev_state.done { let first_selection = selections .iter() .min_by_key(|s| s.id) .context("missing selection for select previous action")?; let last_selection = selections .iter() .max_by_key(|s| s.id) .context("missing selection for select previous action")?; let mut next_selected_range = None; // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer. let bytes_before_last_selection = buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start); let bytes_after_first_selection = buffer.reversed_bytes_in_range(first_selection.end..buffer.len()); let query_matches = query .stream_find_iter(bytes_before_last_selection) .map(|result| (last_selection.start, result)) .chain( query .stream_find_iter(bytes_after_first_selection) .map(|result| (buffer.len(), result)), ); for (end_offset, query_match) in query_matches { let query_match = query_match.context("query match for select previous action")?; let offset_range = end_offset - query_match.end()..end_offset - query_match.start(); if !select_prev_state.wordwise || (!buffer.is_inside_word(offset_range.start, None) && !buffer.is_inside_word(offset_range.end, None)) { next_selected_range = Some(offset_range); break; } } if let Some(next_selected_range) = next_selected_range { self.select_match_ranges( next_selected_range, last_selection.reversed, action.replace_newest, Some(Autoscroll::newest()), window, cx, ); } else { select_prev_state.done = true; } } self.select_prev_state = Some(select_prev_state); } else { let mut only_carets = true; let mut same_text_selected = true; let mut selected_text = None; let mut selections_iter = selections.iter().peekable(); while let Some(selection) = selections_iter.next() { if selection.start != selection.end { only_carets = false; } if same_text_selected { if selected_text.is_none() { selected_text = Some(buffer.text_for_range(selection.range()).collect::<String>()); } if let Some(next_selection) = selections_iter.peek() { if next_selection.len() == selection.len() { let next_selected_text = buffer .text_for_range(next_selection.range()) .collect::<String>(); if Some(next_selected_text) != selected_text { same_text_selected = false; selected_text = None; } } else { same_text_selected = false; selected_text = None; } } } }