Skip to repository content2402 lines · 93.1 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:31:07.552Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
selection.rs
1use super::*;
2
3impl Editor {
4 pub fn sync_selections(
5 &mut self,
6 other: Entity<Editor>,
7 cx: &mut Context<Self>,
8 ) -> gpui::Subscription {
9 let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
10 if !other_selections.is_empty() {
11 self.selections
12 .change_with(&self.display_snapshot(cx), |selections| {
13 selections.select_anchors(other_selections);
14 });
15 }
16
17 let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
18 if let EditorEvent::SelectionsChanged { local: true } = other_evt {
19 let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
20 if other_selections.is_empty() {
21 return;
22 }
23 let snapshot = this.display_snapshot(cx);
24 this.selections.change_with(&snapshot, |selections| {
25 selections.select_anchors(other_selections);
26 });
27 }
28 });
29
30 let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
31 if let EditorEvent::SelectionsChanged { local: true } = this_evt {
32 let these_selections = this.selections.disjoint_anchors().to_vec();
33 if these_selections.is_empty() {
34 return;
35 }
36 other.update(cx, |other_editor, cx| {
37 let snapshot = other_editor.display_snapshot(cx);
38 other_editor
39 .selections
40 .change_with(&snapshot, |selections| {
41 selections.select_anchors(these_selections);
42 })
43 });
44 }
45 });
46
47 Subscription::join(other_subscription, this_subscription)
48 }
49
50 /// Changes selections using the provided mutation function. Changes to `self.selections` occur
51 /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
52 /// effects of selection change occur at the end of the transaction.
53 pub fn change_selections<R>(
54 &mut self,
55 effects: SelectionEffects,
56 window: &mut Window,
57 cx: &mut Context<Self>,
58 change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
59 ) -> R {
60 let snapshot = self.display_snapshot(cx);
61 if let Some(state) = &mut self.deferred_selection_effects_state {
62 state.effects.scroll = effects.scroll.or(state.effects.scroll);
63 state.effects.completions = effects.completions;
64 state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
65 let (changed, result) = self.selections.change_with(&snapshot, change);
66 state.changed |= changed;
67 return result;
68 }
69 let mut state = DeferredSelectionEffectsState {
70 changed: false,
71 effects,
72 old_cursor_position: self.selections.newest_anchor().head(),
73 history_entry: SelectionHistoryEntry {
74 selections: self.selections.disjoint_anchors_arc(),
75 select_next_state: self.select_next_state.clone(),
76 select_prev_state: self.select_prev_state.clone(),
77 add_selections_state: self.add_selections_state.clone(),
78 },
79 };
80 let (changed, result) = self.selections.change_with(&snapshot, change);
81 state.changed = state.changed || changed;
82 if self.defer_selection_effects {
83 self.deferred_selection_effects_state = Some(state);
84 } else {
85 self.apply_selection_effects(state, window, cx);
86 }
87 result
88 }
89
90 /// Defers the effects of selection change, so that the effects of multiple calls to
91 /// `change_selections` are applied at the end. This way these intermediate states aren't added
92 /// to selection history and the state of popovers based on selection position aren't
93 /// erroneously updated.
94 pub fn with_selection_effects_deferred<R>(
95 &mut self,
96 window: &mut Window,
97 cx: &mut Context<Self>,
98 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
99 ) -> R {
100 let already_deferred = self.defer_selection_effects;
101 self.defer_selection_effects = true;
102 let result = update(self, window, cx);
103 if !already_deferred {
104 self.defer_selection_effects = false;
105 if let Some(state) = self.deferred_selection_effects_state.take() {
106 self.apply_selection_effects(state, window, cx);
107 }
108 }
109 result
110 }
111
112 pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
113 self.selections
114 .all_adjusted(snapshot)
115 .iter()
116 .any(|selection| !selection.is_empty())
117 }
118
119 pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
120 if self
121 .selections
122 .pending_anchor()
123 .is_some_and(|pending_selection| {
124 let snapshot = self.buffer().read(cx).snapshot(cx);
125 pending_selection.range().includes(range, &snapshot)
126 })
127 {
128 return true;
129 }
130
131 self.selections
132 .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
133 .into_iter()
134 .any(|selection| {
135 // This is needed to cover a corner case, if we just check for an existing
136 // selection in the fold range, having a cursor at the start of the fold
137 // marks it as selected. Non-empty selections don't cause this.
138 let length = selection.end - selection.start;
139 length > 0
140 })
141 }
142
143 pub fn has_pending_nonempty_selection(&self) -> bool {
144 let pending_nonempty_selection = match self.selections.pending_anchor() {
145 Some(Selection { start, end, .. }) => start != end,
146 None => false,
147 };
148
149 pending_nonempty_selection
150 || (self.columnar_selection_state.is_some()
151 && self.selections.disjoint_anchors().len() > 1)
152 }
153
154 pub fn has_pending_selection(&self) -> bool {
155 self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
156 }
157
158 pub fn set_selections_from_remote(
159 &mut self,
160 selections: Vec<Selection<Anchor>>,
161 pending_selection: Option<Selection<Anchor>>,
162 window: &mut Window,
163 cx: &mut Context<Self>,
164 ) {
165 let old_cursor_position = self.selections.newest_anchor().head();
166 self.selections
167 .change_with(&self.display_snapshot(cx), |s| {
168 s.select_anchors(selections);
169 if let Some(pending_selection) = pending_selection {
170 s.set_pending(pending_selection, SelectMode::Character);
171 } else {
172 s.clear_pending();
173 }
174 });
175 self.selections_did_change(
176 false,
177 &old_cursor_position,
178 SelectionEffects::default(),
179 window,
180 cx,
181 );
182 }
183
184 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
185 if self.selection_mark_mode {
186 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
187 s.move_with(&mut |_, sel| {
188 sel.collapse_to(sel.head(), SelectionGoal::None);
189 });
190 })
191 }
192 self.selection_mark_mode = true;
193 cx.notify();
194 }
195
196 pub fn swap_selection_ends(
197 &mut self,
198 _: &actions::SwapSelectionEnds,
199 window: &mut Window,
200 cx: &mut Context<Self>,
201 ) {
202 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
203 s.move_with(&mut |_, sel| {
204 if sel.start != sel.end {
205 sel.reversed = !sel.reversed
206 }
207 });
208 });
209 self.request_autoscroll(Autoscroll::newest(), cx);
210 cx.notify();
211 }
212
213 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
214 let buffer = self.buffer.read(cx).snapshot(cx);
215 let mut selection = self
216 .selections
217 .first::<MultiBufferOffset>(&self.display_snapshot(cx));
218 selection.set_head(buffer.len(), SelectionGoal::None);
219 self.change_selections(Default::default(), window, cx, |s| {
220 s.select(vec![selection]);
221 });
222 }
223
224 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
225 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
226 s.select_ranges(vec![Anchor::Min..Anchor::Max]);
227 });
228 }
229
230 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
231 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
232 let mut selections = self.selections.all::<Point>(&display_map);
233 let max_point = display_map.buffer_snapshot().max_point();
234 for selection in &mut selections {
235 let rows = selection.spanned_rows(true, &display_map);
236 selection.start = Point::new(rows.start.0, 0);
237 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
238 selection.reversed = false;
239 }
240 self.change_selections(Default::default(), window, cx, |s| {
241 s.select(selections);
242 });
243 }
244
245 pub fn split_selection_into_lines(
246 &mut self,
247 action: &SplitSelectionIntoLines,
248 window: &mut Window,
249 cx: &mut Context<Self>,
250 ) {
251 let selections = self
252 .selections
253 .all::<Point>(&self.display_snapshot(cx))
254 .into_iter()
255 .map(|selection| selection.start..selection.end)
256 .collect::<Vec<_>>();
257 self.unfold_ranges(&selections, true, false, cx);
258
259 let mut new_selection_ranges = Vec::new();
260 {
261 let buffer = self.buffer.read(cx).read(cx);
262 for selection in selections {
263 for row in selection.start.row..selection.end.row {
264 let line_start = Point::new(row, 0);
265 let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
266
267 if action.keep_selections {
268 // Keep the selection range for each line
269 let selection_start = if row == selection.start.row {
270 selection.start
271 } else {
272 line_start
273 };
274 new_selection_ranges.push(selection_start..line_end);
275 } else {
276 // Collapse to cursor at end of line
277 new_selection_ranges.push(line_end..line_end);
278 }
279 }
280
281 let is_multiline_selection = selection.start.row != selection.end.row;
282 // Don't insert last one if it's a multi-line selection ending at the start of a line,
283 // so this action feels more ergonomic when paired with other selection operations
284 let should_skip_last = is_multiline_selection && selection.end.column == 0;
285 if !should_skip_last {
286 if action.keep_selections {
287 if is_multiline_selection {
288 let line_start = Point::new(selection.end.row, 0);
289 new_selection_ranges.push(line_start..selection.end);
290 } else {
291 new_selection_ranges.push(selection.start..selection.end);
292 }
293 } else {
294 new_selection_ranges.push(selection.end..selection.end);
295 }
296 }
297 }
298 }
299 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
300 s.select_ranges(new_selection_ranges);
301 });
302 }
303
304 pub fn add_selection_above(
305 &mut self,
306 action: &AddSelectionAbove,
307 window: &mut Window,
308 cx: &mut Context<Self>,
309 ) {
310 self.add_selection(true, action.skip_soft_wrap, window, cx);
311 }
312
313 pub fn add_selection_below(
314 &mut self,
315 action: &AddSelectionBelow,
316 window: &mut Window,
317 cx: &mut Context<Self>,
318 ) {
319 self.add_selection(false, action.skip_soft_wrap, window, cx);
320 }
321
322 pub fn select_all_matches(
323 &mut self,
324 _action: &SelectAllMatches,
325 window: &mut Window,
326 cx: &mut Context<Self>,
327 ) -> Result<()> {
328 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
329
330 self.select_next_match_internal(&display_map, false, None, window, cx)?;
331 let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
332 else {
333 return Ok(());
334 };
335
336 let mut new_selections = Vec::new();
337 let initial_selection = self.selections.oldest::<MultiBufferOffset>(&display_map);
338 let reversed = initial_selection.reversed;
339 let buffer = display_map.buffer_snapshot();
340 let query_matches = select_next_state
341 .query
342 .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
343
344 for query_match in query_matches.into_iter() {
345 let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
346 let offset_range = if reversed {
347 MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
348 } else {
349 MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
350 };
351
352 let is_partial_word_match = select_next_state.wordwise
353 && (buffer.is_inside_word(offset_range.start, None)
354 || buffer.is_inside_word(offset_range.end, None));
355
356 let is_initial_selection = MultiBufferOffset(query_match.start())
357 == initial_selection.start
358 && MultiBufferOffset(query_match.end()) == initial_selection.end;
359
360 if !is_partial_word_match && !is_initial_selection {
361 new_selections.push(offset_range);
362 }
363 }
364
365 // Ensure that the initial range is the last selection, as
366 // `MutableSelectionsCollection::select_ranges` makes the last selection
367 // the newest selection, which the editor then relies on as the primary
368 // cursor for scroll targeting. Without this, the last match would then
369 // be automatically focused when the user started editing the selected
370 // matches.
371 let initial_directed_range = if reversed {
372 initial_selection.end..initial_selection.start
373 } else {
374 initial_selection.start..initial_selection.end
375 };
376 new_selections.push(initial_directed_range);
377
378 select_next_state.done = true;
379 self.unfold_ranges(&new_selections, false, false, cx);
380 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
381 selections.select_ranges(new_selections)
382 });
383
384 Ok(())
385 }
386
387 pub fn select_next(
388 &mut self,
389 action: &SelectNext,
390 window: &mut Window,
391 cx: &mut Context<Self>,
392 ) -> Result<()> {
393 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
394 self.select_next_match_internal(
395 &display_map,
396 action.replace_newest,
397 Some(Autoscroll::newest()),
398 window,
399 cx,
400 )
401 }
402
403 pub fn select_previous(
404 &mut self,
405 action: &SelectPrevious,
406 window: &mut Window,
407 cx: &mut Context<Self>,
408 ) -> Result<()> {
409 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
410 let buffer = display_map.buffer_snapshot();
411 let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
412 if let Some(mut select_prev_state) = self.select_prev_state.take() {
413 let query = &select_prev_state.query;
414 if !select_prev_state.done {
415 let first_selection = selections
416 .iter()
417 .min_by_key(|s| s.id)
418 .context("missing selection for select previous action")?;
419 let last_selection = selections
420 .iter()
421 .max_by_key(|s| s.id)
422 .context("missing selection for select previous action")?;
423 let mut next_selected_range = None;
424 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
425 let bytes_before_last_selection =
426 buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
427 let bytes_after_first_selection =
428 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
429 let query_matches = query
430 .stream_find_iter(bytes_before_last_selection)
431 .map(|result| (last_selection.start, result))
432 .chain(
433 query
434 .stream_find_iter(bytes_after_first_selection)
435 .map(|result| (buffer.len(), result)),
436 );
437 for (end_offset, query_match) in query_matches {
438 let query_match =
439 query_match.context("query match for select previous action")?;
440 let offset_range =
441 end_offset - query_match.end()..end_offset - query_match.start();
442
443 if !select_prev_state.wordwise
444 || (!buffer.is_inside_word(offset_range.start, None)
445 && !buffer.is_inside_word(offset_range.end, None))
446 {
447 next_selected_range = Some(offset_range);
448 break;
449 }
450 }
451
452 if let Some(next_selected_range) = next_selected_range {
453 self.select_match_ranges(
454 next_selected_range,
455 last_selection.reversed,
456 action.replace_newest,
457 Some(Autoscroll::newest()),
458 window,
459 cx,
460 );
461 } else {
462 select_prev_state.done = true;
463 }
464 }
465
466 self.select_prev_state = Some(select_prev_state);
467 } else {
468 let mut only_carets = true;
469 let mut same_text_selected = true;
470 let mut selected_text = None;
471
472 let mut selections_iter = selections.iter().peekable();
473 while let Some(selection) = selections_iter.next() {
474 if selection.start != selection.end {
475 only_carets = false;
476 }
477
478 if same_text_selected {
479 if selected_text.is_none() {
480 selected_text =
481 Some(buffer.text_for_range(selection.range()).collect::<String>());
482 }
483
484 if let Some(next_selection) = selections_iter.peek() {
485 if next_selection.len() == selection.len() {
486 let next_selected_text = buffer
487 .text_for_range(next_selection.range())
488 .collect::<String>();
489 if Some(next_selected_text) != selected_text {
490 same_text_selected = false;
491 selected_text = None;
492 }
493 } else {
494 same_text_selected = false;
495 selected_text = None;
496 }
497 }
498 }
499 }
500
501 if only_carets {
502 for selection in &mut selections {
503 let (word_range, _) = buffer.surrounding_word(selection.start, None);
504 selection.start = word_range.start;
505 selection.end = word_range.end;
506 selection.goal = SelectionGoal::None;
507 selection.reversed = false;
508 self.select_match_ranges(
509 selection.start..selection.end,
510 selection.reversed,
511 action.replace_newest,
512 Some(Autoscroll::newest()),
513 window,
514 cx,
515 );
516 }
517 if selections.len() == 1 {
518 let selection = selections
519 .last()
520 .expect("ensured that there's only one selection");
521 let query = buffer
522 .text_for_range(selection.start..selection.end)
523 .collect::<String>();
524 let is_empty = query.is_empty();
525 let select_state = SelectNextState {
526 query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
527 wordwise: true,
528 done: is_empty,
529 };
530 self.select_prev_state = Some(select_state);
531 } else {
532 self.select_prev_state = None;
533 }
534 } else if let Some(selected_text) = selected_text {
535 self.select_prev_state = Some(SelectNextState {
536 query: self
537 .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
538 wordwise: false,
539 done: false,
540 });
541 self.select_previous(action, window, cx)?;
542 }
543 }
544 Ok(())
545 }
546
547 pub fn find_next_match(
548 &mut self,
549 _: &FindNextMatch,
550 window: &mut Window,
551 cx: &mut Context<Self>,
552 ) -> Result<()> {
553 let selections = self.selections.disjoint_anchors_arc();
554 match selections.first() {
555 Some(first) if selections.len() >= 2 => {
556 self.change_selections(Default::default(), window, cx, |s| {
557 s.select_ranges([first.range()]);
558 });
559 }
560 _ => self.select_next(
561 &SelectNext {
562 replace_newest: true,
563 },
564 window,
565 cx,
566 )?,
567 }
568 Ok(())
569 }
570
571 pub fn find_previous_match(
572 &mut self,
573 _: &FindPreviousMatch,
574 window: &mut Window,
575 cx: &mut Context<Self>,
576 ) -> Result<()> {
577 let selections = self.selections.disjoint_anchors_arc();
578 match selections.last() {
579 Some(last) if selections.len() >= 2 => {
580 self.change_selections(Default::default(), window, cx, |s| {
581 s.select_ranges([last.range()]);
582 });
583 }
584 _ => self.select_previous(
585 &SelectPrevious {
586 replace_newest: true,
587 },
588 window,
589 cx,
590 )?,
591 }
592 Ok(())
593 }
594
595 pub fn select_enclosing_symbol(
596 &mut self,
597 _: &SelectEnclosingSymbol,
598 window: &mut Window,
599 cx: &mut Context<Self>,
600 ) {
601 let buffer = self.buffer.read(cx).snapshot(cx);
602 let old_selections = self
603 .selections
604 .all::<MultiBufferOffset>(&self.display_snapshot(cx))
605 .into_boxed_slice();
606
607 fn update_selection(
608 selection: &Selection<MultiBufferOffset>,
609 buffer_snap: &MultiBufferSnapshot,
610 ) -> Option<Selection<MultiBufferOffset>> {
611 let cursor = selection.head();
612 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
613 for symbol in symbols.iter().rev() {
614 let start = symbol.range.start.to_offset(buffer_snap);
615 let end = symbol.range.end.to_offset(buffer_snap);
616 let new_range = start..end;
617 if start < selection.start || end > selection.end {
618 return Some(Selection {
619 id: selection.id,
620 start: new_range.start,
621 end: new_range.end,
622 goal: SelectionGoal::None,
623 reversed: selection.reversed,
624 });
625 }
626 }
627 None
628 }
629
630 let mut selected_larger_symbol = false;
631 let new_selections = old_selections
632 .iter()
633 .map(|selection| match update_selection(selection, &buffer) {
634 Some(new_selection) => {
635 if new_selection.range() != selection.range() {
636 selected_larger_symbol = true;
637 }
638 new_selection
639 }
640 None => selection.clone(),
641 })
642 .collect::<Vec<_>>();
643
644 if selected_larger_symbol {
645 self.change_selections(Default::default(), window, cx, |s| {
646 s.select(new_selections);
647 });
648 }
649 }
650
651 pub fn select_larger_syntax_node(
652 &mut self,
653 _: &SelectLargerSyntaxNode,
654 window: &mut Window,
655 cx: &mut Context<Self>,
656 ) {
657 let Some(visible_row_count) = self.visible_row_count() else {
658 return;
659 };
660 let old_selections: Box<[_]> = self
661 .selections
662 .all::<MultiBufferOffset>(&self.display_snapshot(cx))
663 .into();
664 if old_selections.is_empty() {
665 return;
666 }
667
668 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
669 let buffer = self.buffer.read(cx).snapshot(cx);
670
671 let mut selected_larger_node = false;
672 let mut new_selections = old_selections
673 .iter()
674 .map(|selection| {
675 let old_range = selection.start..selection.end;
676
677 if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
678 // manually select word at selection
679 if ["string_content", "inline"].contains(&node.kind()) {
680 let (word_range, _) = buffer.surrounding_word(old_range.start, None);
681 // ignore if word is already selected
682 if !word_range.is_empty() && old_range != word_range {
683 let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
684 // only select word if start and end point belongs to same word
685 if word_range == last_word_range {
686 selected_larger_node = true;
687 return Selection {
688 id: selection.id,
689 start: word_range.start,
690 end: word_range.end,
691 goal: SelectionGoal::None,
692 reversed: selection.reversed,
693 };
694 }
695 }
696 }
697 }
698
699 let mut new_range = old_range.clone();
700 while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
701 new_range = range;
702 if !node.is_named() {
703 continue;
704 }
705 if !display_map.intersects_fold(new_range.start)
706 && !display_map.intersects_fold(new_range.end)
707 {
708 break;
709 }
710 }
711
712 selected_larger_node |= new_range != old_range;
713 Selection {
714 id: selection.id,
715 start: new_range.start,
716 end: new_range.end,
717 goal: SelectionGoal::None,
718 reversed: selection.reversed,
719 }
720 })
721 .collect::<Vec<_>>();
722
723 if !selected_larger_node {
724 return; // don't put this call in the history
725 }
726
727 // scroll based on transformation done to the last selection created by the user
728 let (last_old, last_new) = old_selections
729 .last()
730 .zip(new_selections.last().cloned())
731 .expect("old_selections isn't empty");
732
733 let is_selection_reversed = if new_selections.len() == 1 {
734 let should_be_reversed = last_old.start != last_new.start;
735 new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
736 should_be_reversed
737 } else {
738 last_new.reversed
739 };
740
741 self.select_syntax_node_history.disable_clearing = true;
742 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
743 s.select(new_selections.clone());
744 });
745 self.select_syntax_node_history.disable_clearing = false;
746
747 let start_row = last_new.start.to_display_point(&display_map).row().0;
748 let end_row = last_new.end.to_display_point(&display_map).row().0;
749 let selection_height = end_row - start_row + 1;
750 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
751
752 let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
753 let scroll_behavior = if fits_on_the_screen {
754 self.request_autoscroll(Autoscroll::fit(), cx);
755 SelectSyntaxNodeScrollBehavior::FitSelection
756 } else if is_selection_reversed {
757 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
758 SelectSyntaxNodeScrollBehavior::CursorTop
759 } else {
760 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
761 SelectSyntaxNodeScrollBehavior::CursorBottom
762 };
763
764 let old_selections: Box<[Selection<Anchor>]> = old_selections
765 .iter()
766 .map(|s| s.map(|offset| buffer.anchor_before(offset)))
767 .collect();
768 self.select_syntax_node_history.push((
769 old_selections,
770 scroll_behavior,
771 is_selection_reversed,
772 ));
773 }
774
775 pub fn select_smaller_syntax_node(
776 &mut self,
777 _: &SelectSmallerSyntaxNode,
778 window: &mut Window,
779 cx: &mut Context<Self>,
780 ) {
781 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
782 self.select_syntax_node_history.pop()
783 {
784 if let Some(selection) = selections.last_mut() {
785 selection.reversed = is_selection_reversed;
786 }
787
788 let snapshot = self.buffer.read(cx).snapshot(cx);
789 let selections: Vec<Selection<MultiBufferOffset>> = selections
790 .iter()
791 .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
792 .collect();
793
794 self.select_syntax_node_history.disable_clearing = true;
795 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
796 s.select(selections);
797 });
798 self.select_syntax_node_history.disable_clearing = false;
799
800 match scroll_behavior {
801 SelectSyntaxNodeScrollBehavior::CursorTop => {
802 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
803 }
804 SelectSyntaxNodeScrollBehavior::FitSelection => {
805 self.request_autoscroll(Autoscroll::fit(), cx);
806 }
807 SelectSyntaxNodeScrollBehavior::CursorBottom => {
808 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
809 }
810 }
811 }
812 }
813
814 pub fn select_next_syntax_node(
815 &mut self,
816 _: &SelectNextSyntaxNode,
817 window: &mut Window,
818 cx: &mut Context<Self>,
819 ) {
820 let old_selections = self.selections.all_anchors(&self.display_snapshot(cx));
821 if old_selections.is_empty() {
822 return;
823 }
824
825 let buffer = self.buffer.read(cx).snapshot(cx);
826 let mut selected_sibling = false;
827
828 let new_selections = old_selections
829 .iter()
830 .map(|selection| {
831 let old_range =
832 selection.start.to_offset(&buffer)..selection.end.to_offset(&buffer);
833 if let Some(results) = buffer.map_excerpt_ranges(
834 old_range,
835 |buf, _excerpt_range, input_buffer_range| {
836 let Some(node) = buf.syntax_next_sibling(input_buffer_range) else {
837 return Vec::new();
838 };
839 vec![(
840 BufferOffset(node.byte_range().start)
841 ..BufferOffset(node.byte_range().end),
842 (),
843 )]
844 },
845 ) && let [(new_range, _)] = results.as_slice()
846 {
847 selected_sibling = true;
848 let new_range =
849 buffer.anchor_after(new_range.start)..buffer.anchor_before(new_range.end);
850 Selection {
851 id: selection.id,
852 start: new_range.start,
853 end: new_range.end,
854 goal: SelectionGoal::None,
855 reversed: selection.reversed,
856 }
857 } else {
858 selection.clone()
859 }
860 })
861 .collect::<Vec<_>>();
862
863 if selected_sibling {
864 self.change_selections(
865 SelectionEffects::scroll(Autoscroll::fit()),
866 window,
867 cx,
868 |s| {
869 s.select(new_selections);
870 },
871 );
872 }
873 }
874
875 pub fn select_prev_syntax_node(
876 &mut self,
877 _: &SelectPreviousSyntaxNode,
878 window: &mut Window,
879 cx: &mut Context<Self>,
880 ) {
881 let old_selections: Arc<[_]> = self.selections.all_anchors(&self.display_snapshot(cx));
882
883 let multibuffer_snapshot = self.buffer.read(cx).snapshot(cx);
884 let mut selected_sibling = false;
885
886 let new_selections = old_selections
887 .iter()
888 .map(|selection| {
889 let old_range = selection.start.to_offset(&multibuffer_snapshot)
890 ..selection.end.to_offset(&multibuffer_snapshot);
891 if let Some(results) = multibuffer_snapshot.map_excerpt_ranges(
892 old_range,
893 |buf, _excerpt_range, input_buffer_range| {
894 let Some(node) = buf.syntax_prev_sibling(input_buffer_range) else {
895 return Vec::new();
896 };
897 vec![(
898 BufferOffset(node.byte_range().start)
899 ..BufferOffset(node.byte_range().end),
900 (),
901 )]
902 },
903 ) && let [(new_range, _)] = results.as_slice()
904 {
905 selected_sibling = true;
906 let new_range = multibuffer_snapshot.anchor_after(new_range.start)
907 ..multibuffer_snapshot.anchor_before(new_range.end);
908 Selection {
909 id: selection.id,
910 start: new_range.start,
911 end: new_range.end,
912 goal: SelectionGoal::None,
913 reversed: selection.reversed,
914 }
915 } else {
916 selection.clone()
917 }
918 })
919 .collect::<Vec<_>>();
920
921 if selected_sibling {
922 self.change_selections(
923 SelectionEffects::scroll(Autoscroll::fit()),
924 window,
925 cx,
926 |s| {
927 s.select(new_selections);
928 },
929 );
930 }
931 }
932
933 pub fn move_to_start_of_larger_syntax_node(
934 &mut self,
935 _: &MoveToStartOfLargerSyntaxNode,
936 window: &mut Window,
937 cx: &mut Context<Self>,
938 ) {
939 self.move_cursors_to_syntax_nodes(window, cx, false);
940 }
941
942 pub fn move_to_end_of_larger_syntax_node(
943 &mut self,
944 _: &MoveToEndOfLargerSyntaxNode,
945 window: &mut Window,
946 cx: &mut Context<Self>,
947 ) {
948 self.move_cursors_to_syntax_nodes(window, cx, true);
949 }
950
951 pub fn select_to_start_of_larger_syntax_node(
952 &mut self,
953 _: &SelectToStartOfLargerSyntaxNode,
954 window: &mut Window,
955 cx: &mut Context<Self>,
956 ) {
957 self.select_to_syntax_nodes(window, cx, false);
958 }
959
960 pub fn select_to_end_of_larger_syntax_node(
961 &mut self,
962 _: &SelectToEndOfLargerSyntaxNode,
963 window: &mut Window,
964 cx: &mut Context<Self>,
965 ) {
966 self.select_to_syntax_nodes(window, cx, true);
967 }
968
969 pub fn select_inside_delimiters(
970 &mut self,
971 _: &SelectInsideDelimiters,
972 window: &mut Window,
973 cx: &mut Context<Self>,
974 ) {
975 self.select_delimiters_impl(false, window, cx);
976 }
977
978 pub fn select_around_delimiters(
979 &mut self,
980 _: &SelectAroundDelimiters,
981 window: &mut Window,
982 cx: &mut Context<Self>,
983 ) {
984 self.select_delimiters_impl(true, window, cx);
985 }
986
987 fn select_delimiters_impl(
988 &mut self,
989 include_brackets: bool,
990 window: &mut Window,
991 cx: &mut Context<Self>,
992 ) {
993 self.change_selections(Default::default(), window, cx, |s| {
994 s.move_offsets_with(&mut |snapshot, selection| {
995 let Some(enclosing_bracket_ranges) =
996 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
997 else {
998 return;
999 };
1000
1001 let mut best = None;
1002 let mut best_length = usize::MAX;
1003
1004 for (open, close) in enclosing_bracket_ranges {
1005 let range = if include_brackets {
1006 open.start..close.end
1007 } else {
1008 open.end..close.start
1009 };
1010
1011 // Skip any bracket pair that is already covered by the
1012 // selection so repeated uses of delimiters selection only
1013 // evere expands outwards to the next pair.
1014 if (selection.start..selection.end).contains_inclusive(&range) {
1015 continue;
1016 }
1017
1018 let length = close.end - open.start;
1019 if length < best_length {
1020 best_length = length;
1021 best = Some(range);
1022 }
1023 }
1024
1025 if let Some(range) = best {
1026 selection.set_head_tail(range.end, range.start, SelectionGoal::None);
1027 }
1028 })
1029 });
1030 }
1031
1032 pub fn move_to_enclosing_bracket(
1033 &mut self,
1034 _: &MoveToEnclosingBracket,
1035 window: &mut Window,
1036 cx: &mut Context<Self>,
1037 ) {
1038 self.change_selections(Default::default(), window, cx, |s| {
1039 s.move_offsets_with(&mut |snapshot, selection| {
1040 let Some(enclosing_bracket_ranges) =
1041 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
1042 else {
1043 return;
1044 };
1045
1046 let mut best_length = usize::MAX;
1047 let mut best_inside = false;
1048 let mut best_in_bracket_range = false;
1049 let mut best_destination = None;
1050 for (open, close) in enclosing_bracket_ranges {
1051 let close = close.to_inclusive();
1052 let length = *close.end() - open.start;
1053 let inside = selection.start >= open.end && selection.end <= *close.start();
1054 let in_bracket_range = open.to_inclusive().contains(&selection.head())
1055 || close.contains(&selection.head());
1056
1057 // If best is next to a bracket and current isn't, skip
1058 if !in_bracket_range && best_in_bracket_range {
1059 continue;
1060 }
1061
1062 // Prefer smaller lengths unless best is inside and current isn't
1063 if length > best_length && (best_inside || !inside) {
1064 continue;
1065 }
1066
1067 best_length = length;
1068 best_inside = inside;
1069 best_in_bracket_range = in_bracket_range;
1070 best_destination = Some(
1071 if close.contains(&selection.start) && close.contains(&selection.end) {
1072 if inside { open.end } else { open.start }
1073 } else if inside {
1074 *close.start()
1075 } else {
1076 *close.end()
1077 },
1078 );
1079 }
1080
1081 if let Some(destination) = best_destination {
1082 selection.collapse_to(destination, SelectionGoal::None);
1083 }
1084 })
1085 });
1086 }
1087
1088 pub fn undo_selection(
1089 &mut self,
1090 _: &UndoSelection,
1091 window: &mut Window,
1092 cx: &mut Context<Self>,
1093 ) {
1094 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
1095 self.selection_history.mode = SelectionHistoryMode::Undoing;
1096 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
1097 this.end_selection(window, cx);
1098 this.change_selections(
1099 SelectionEffects::scroll(Autoscroll::newest()),
1100 window,
1101 cx,
1102 |s| s.select_anchors(entry.selections.to_vec()),
1103 );
1104 });
1105 self.selection_history.mode = SelectionHistoryMode::Normal;
1106
1107 self.select_next_state = entry.select_next_state;
1108 self.select_prev_state = entry.select_prev_state;
1109 self.add_selections_state = entry.add_selections_state;
1110 }
1111 }
1112
1113 pub fn redo_selection(
1114 &mut self,
1115 _: &RedoSelection,
1116 window: &mut Window,
1117 cx: &mut Context<Self>,
1118 ) {
1119 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
1120 self.selection_history.mode = SelectionHistoryMode::Redoing;
1121 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
1122 this.end_selection(window, cx);
1123 this.change_selections(
1124 SelectionEffects::scroll(Autoscroll::newest()),
1125 window,
1126 cx,
1127 |s| s.select_anchors(entry.selections.to_vec()),
1128 );
1129 });
1130 self.selection_history.mode = SelectionHistoryMode::Normal;
1131
1132 self.select_next_state = entry.select_next_state;
1133 self.select_prev_state = entry.select_prev_state;
1134 self.add_selections_state = entry.add_selections_state;
1135 }
1136 }
1137
1138 pub(super) fn select(
1139 &mut self,
1140 phase: SelectPhase,
1141 window: &mut Window,
1142 cx: &mut Context<Self>,
1143 ) {
1144 self.hide_context_menu(window, cx);
1145
1146 match phase {
1147 SelectPhase::Begin {
1148 position,
1149 add,
1150 click_count,
1151 } => self.begin_selection(position, add, click_count, window, cx),
1152 SelectPhase::BeginColumnar {
1153 position,
1154 goal_column,
1155 reset,
1156 mode,
1157 } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
1158 SelectPhase::Extend {
1159 position,
1160 click_count,
1161 } => self.extend_selection(position, click_count, window, cx),
1162 SelectPhase::Update {
1163 position,
1164 goal_column,
1165 scroll_delta,
1166 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
1167 SelectPhase::End => self.end_selection(window, cx),
1168 }
1169 }
1170
1171 pub(super) fn extend_selection(
1172 &mut self,
1173 position: DisplayPoint,
1174 click_count: usize,
1175 window: &mut Window,
1176 cx: &mut Context<Self>,
1177 ) {
1178 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1179 let tail = self
1180 .selections
1181 .newest::<MultiBufferOffset>(&display_map)
1182 .tail();
1183 let click_count = click_count.max(match self.selections.select_mode() {
1184 SelectMode::Character => 1,
1185 SelectMode::Word(_) => 2,
1186 SelectMode::Line(_) => 3,
1187 SelectMode::All => 4,
1188 });
1189 self.begin_selection(position, false, click_count, window, cx);
1190
1191 let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
1192
1193 let current_selection = match self.selections.select_mode() {
1194 SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
1195 SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
1196 };
1197
1198 let Some((mut pending_selection, mut pending_mode)) = self.pending_selection_and_mode()
1199 else {
1200 log::error!("extend_selection dispatched with no pending selection");
1201 return;
1202 };
1203
1204 if pending_selection
1205 .start
1206 .cmp(¤t_selection.start, display_map.buffer_snapshot())
1207 == Ordering::Greater
1208 {
1209 pending_selection.start = current_selection.start;
1210 }
1211 if pending_selection
1212 .end
1213 .cmp(¤t_selection.end, display_map.buffer_snapshot())
1214 == Ordering::Less
1215 {
1216 pending_selection.end = current_selection.end;
1217 pending_selection.reversed = true;
1218 }
1219
1220 match &mut pending_mode {
1221 SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
1222 _ => {}
1223 }
1224
1225 let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
1226 SelectionEffects::scroll(Autoscroll::fit())
1227 } else {
1228 SelectionEffects::no_scroll()
1229 };
1230
1231 self.change_selections(effects, window, cx, |s| {
1232 s.set_pending(pending_selection.clone(), pending_mode);
1233 s.set_is_extending(true);
1234 });
1235 }
1236
1237 pub(super) fn begin_selection(
1238 &mut self,
1239 position: DisplayPoint,
1240 add: bool,
1241 click_count: usize,
1242 window: &mut Window,
1243 cx: &mut Context<Self>,
1244 ) {
1245 if !self.focus_handle.is_focused(window) {
1246 self.last_focused_descendant = None;
1247 window.focus(&self.focus_handle, cx);
1248 }
1249
1250 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1251 let buffer = display_map.buffer_snapshot();
1252 let position = display_map.clip_point(position, Bias::Left);
1253
1254 let start;
1255 let end;
1256 let mode;
1257 let mut auto_scroll;
1258 match click_count {
1259 1 => {
1260 start = buffer.anchor_before(position.to_point(&display_map));
1261 end = start;
1262 mode = SelectMode::Character;
1263 auto_scroll = true;
1264 }
1265 2 => {
1266 let position = display_map
1267 .clip_point(position, Bias::Left)
1268 .to_offset(&display_map, Bias::Left);
1269 let (range, _) = buffer.surrounding_word(position, None);
1270 start = buffer.anchor_before(range.start);
1271 end = buffer.anchor_before(range.end);
1272 mode = SelectMode::Word(start..end);
1273 auto_scroll = true;
1274 }
1275 3 => {
1276 let position = display_map
1277 .clip_point(position, Bias::Left)
1278 .to_point(&display_map);
1279 let line_start = display_map.prev_line_boundary(position).0;
1280 let next_line_start = buffer.clip_point(
1281 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1282 Bias::Left,
1283 );
1284 start = buffer.anchor_before(line_start);
1285 end = buffer.anchor_before(next_line_start);
1286 mode = SelectMode::Line(start..end);
1287 auto_scroll = true;
1288 }
1289 _ => {
1290 start = buffer.anchor_before(MultiBufferOffset(0));
1291 end = buffer.anchor_before(buffer.len());
1292 mode = SelectMode::All;
1293 auto_scroll = false;
1294 }
1295 }
1296 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
1297
1298 let point_to_delete: Option<usize> = {
1299 let selected_points: Vec<Selection<Point>> =
1300 self.selections.disjoint_in_range(start..end, &display_map);
1301
1302 if !add || click_count > 1 {
1303 None
1304 } else if !selected_points.is_empty() {
1305 Some(selected_points[0].id)
1306 } else {
1307 let clicked_point_already_selected =
1308 self.selections.disjoint_anchors().iter().find(|selection| {
1309 selection.start.to_point(buffer) == start.to_point(buffer)
1310 || selection.end.to_point(buffer) == end.to_point(buffer)
1311 });
1312
1313 clicked_point_already_selected.map(|selection| selection.id)
1314 }
1315 };
1316
1317 let selections_count = self.selections.count();
1318 let effects = if auto_scroll {
1319 SelectionEffects::default()
1320 } else {
1321 SelectionEffects::no_scroll()
1322 };
1323
1324 self.change_selections(effects, window, cx, |s| {
1325 if let Some(point_to_delete) = point_to_delete {
1326 s.delete(point_to_delete);
1327
1328 if selections_count == 1 {
1329 s.set_pending_anchor_range(start..end, mode);
1330 }
1331 } else {
1332 if !add {
1333 s.clear_disjoint();
1334 }
1335
1336 s.set_pending_anchor_range(start..end, mode);
1337 }
1338 });
1339 }
1340
1341 pub(super) fn update_selection(
1342 &mut self,
1343 position: DisplayPoint,
1344 goal_column: u32,
1345 scroll_delta: gpui::Point<f32>,
1346 window: &mut Window,
1347 cx: &mut Context<Self>,
1348 ) {
1349 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1350
1351 if self.columnar_selection_state.is_some() {
1352 self.select_columns(position, goal_column, &display_map, window, cx);
1353 } else if let Some((mut pending, mode)) = self.pending_selection_and_mode() {
1354 let buffer = display_map.buffer_snapshot();
1355 let head;
1356 let tail;
1357 match &mode {
1358 SelectMode::Character => {
1359 head = position.to_point(&display_map);
1360 tail = pending.tail().to_point(buffer);
1361 }
1362 SelectMode::Word(original_range) => {
1363 let offset = display_map
1364 .clip_point(position, Bias::Left)
1365 .to_offset(&display_map, Bias::Left);
1366 let original_range = original_range.to_offset(buffer);
1367
1368 let head_offset = if buffer.is_inside_word(offset, None)
1369 || original_range.contains(&offset)
1370 {
1371 let (word_range, _) = buffer.surrounding_word(offset, None);
1372 if word_range.start < original_range.start {
1373 word_range.start
1374 } else {
1375 word_range.end
1376 }
1377 } else {
1378 offset
1379 };
1380
1381 head = head_offset.to_point(buffer);
1382 if head_offset <= original_range.start {
1383 tail = original_range.end.to_point(buffer);
1384 } else {
1385 tail = original_range.start.to_point(buffer);
1386 }
1387 }
1388 SelectMode::Line(original_range) => {
1389 let original_range = original_range.to_point(display_map.buffer_snapshot());
1390
1391 let position = display_map
1392 .clip_point(position, Bias::Left)
1393 .to_point(&display_map);
1394 let line_start = display_map.prev_line_boundary(position).0;
1395 let next_line_start = buffer.clip_point(
1396 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1397 Bias::Left,
1398 );
1399
1400 if line_start < original_range.start {
1401 head = line_start
1402 } else {
1403 head = next_line_start
1404 }
1405
1406 if head <= original_range.start {
1407 tail = original_range.end;
1408 } else {
1409 tail = original_range.start;
1410 }
1411 }
1412 SelectMode::All => {
1413 return;
1414 }
1415 };
1416
1417 if head < tail {
1418 pending.start = buffer.anchor_before(head);
1419 pending.end = buffer.anchor_before(tail);
1420 pending.reversed = true;
1421 } else {
1422 pending.start = buffer.anchor_before(tail);
1423 pending.end = buffer.anchor_before(head);
1424 pending.reversed = false;
1425 }
1426
1427 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
1428 s.set_pending(pending.clone(), mode);
1429 });
1430 } else {
1431 log::error!("update_selection dispatched with no pending selection");
1432 return;
1433 }
1434
1435 self.apply_scroll_delta(scroll_delta, window, cx);
1436 cx.notify();
1437 }
1438
1439 pub(super) fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1440 self.columnar_selection_state.take();
1441 if let Some(pending_mode) = self.selections.pending_mode() {
1442 let selections = self
1443 .selections
1444 .all::<MultiBufferOffset>(&self.display_snapshot(cx));
1445 // `select` below resets the selections' granularity to `Character`, since it's the
1446 // funnel every wholesale selection replacement goes through. When extending, the
1447 // granularity established by the selection gesture that started the extension must
1448 // survive that reset instead of being overwritten by `pending_mode`.
1449 let select_mode = if self.selections.is_extending() {
1450 self.selections.select_mode().clone()
1451 } else {
1452 pending_mode
1453 };
1454 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
1455 s.select(selections);
1456 s.clear_pending();
1457 s.set_is_extending(false);
1458 s.set_select_mode(select_mode);
1459 });
1460 }
1461 }
1462
1463 fn begin_columnar_selection(
1464 &mut self,
1465 position: DisplayPoint,
1466 goal_column: u32,
1467 reset: bool,
1468 mode: ColumnarMode,
1469 window: &mut Window,
1470 cx: &mut Context<Self>,
1471 ) {
1472 if !self.focus_handle.is_focused(window) {
1473 self.last_focused_descendant = None;
1474 window.focus(&self.focus_handle, cx);
1475 }
1476
1477 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1478
1479 if reset {
1480 let pointer_position = display_map
1481 .buffer_snapshot()
1482 .anchor_before(position.to_point(&display_map));
1483
1484 self.change_selections(
1485 SelectionEffects::scroll(Autoscroll::newest()),
1486 window,
1487 cx,
1488 |s| {
1489 s.clear_disjoint();
1490 s.set_pending_anchor_range(
1491 pointer_position..pointer_position,
1492 SelectMode::Character,
1493 );
1494 },
1495 );
1496 };
1497
1498 let tail = self.selections.newest::<Point>(&display_map).tail();
1499 let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
1500 self.columnar_selection_state = match mode {
1501 ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
1502 selection_tail: selection_anchor,
1503 display_point: if reset {
1504 if position.column() != goal_column {
1505 Some(DisplayPoint::new(position.row(), goal_column))
1506 } else {
1507 None
1508 }
1509 } else {
1510 None
1511 },
1512 }),
1513 ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
1514 selection_tail: selection_anchor,
1515 }),
1516 };
1517
1518 if !reset {
1519 self.select_columns(position, goal_column, &display_map, window, cx);
1520 }
1521 }
1522
1523 fn selections_did_change(
1524 &mut self,
1525 local: bool,
1526 old_cursor_position: &Anchor,
1527 effects: SelectionEffects,
1528 window: &mut Window,
1529 cx: &mut Context<Self>,
1530 ) {
1531 self.last_selection_from_search = effects.from_search;
1532 window.invalidate_character_coordinates();
1533
1534 // Copy selections to primary selection buffer
1535 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1536 if local {
1537 let selections = self
1538 .selections
1539 .all::<MultiBufferOffset>(&self.display_snapshot(cx));
1540 let buffer_handle = self.buffer.read(cx).read(cx);
1541
1542 let mut text = String::new();
1543 for (index, selection) in selections.iter().enumerate() {
1544 let text_for_selection = buffer_handle
1545 .text_for_range(selection.start..selection.end)
1546 .collect::<String>();
1547
1548 text.push_str(&text_for_selection);
1549 if index != selections.len() - 1 {
1550 text.push('\n');
1551 }
1552 }
1553
1554 if !text.is_empty() {
1555 cx.write_to_primary(ClipboardItem::new_string(text));
1556 }
1557 }
1558
1559 let selection_anchors = self.selections.disjoint_anchors_arc();
1560
1561 if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
1562 self.buffer.update(cx, |buffer, cx| {
1563 buffer.set_active_selections(
1564 &selection_anchors,
1565 self.selections.line_mode(),
1566 self.cursor_shape,
1567 cx,
1568 )
1569 });
1570 }
1571 let display_map = self
1572 .display_map
1573 .update(cx, |display_map, cx| display_map.snapshot(cx));
1574 let buffer = display_map.buffer_snapshot();
1575 if self.selections.count() == 1 {
1576 self.add_selections_state = None;
1577 }
1578 self.select_next_state = None;
1579 self.select_prev_state = None;
1580 self.select_syntax_node_history.try_clear();
1581 self.invalidate_autoclose_regions(&selection_anchors, buffer);
1582 self.snippet_stack.invalidate(&selection_anchors, buffer);
1583 self.take_rename(false, window, cx);
1584
1585 let newest_selection = self.selections.newest_anchor();
1586 let new_cursor_position = newest_selection.head();
1587 let selection_start = newest_selection.start;
1588
1589 if effects.nav_history.is_none() || effects.nav_history == Some(true) {
1590 self.push_to_nav_history(
1591 *old_cursor_position,
1592 Some(new_cursor_position.to_point(buffer)),
1593 false,
1594 effects.nav_history == Some(true),
1595 cx,
1596 );
1597 }
1598
1599 if local {
1600 if let Some((anchor, _)) = buffer.anchor_to_buffer_anchor(new_cursor_position) {
1601 self.register_buffer(anchor.buffer_id, cx);
1602 }
1603
1604 let mut context_menu = self.context_menu.borrow_mut();
1605 let completion_menu = match context_menu.as_ref() {
1606 Some(CodeContextMenu::Completions(menu)) => Some(menu),
1607 Some(CodeContextMenu::CodeActions(_)) => {
1608 *context_menu = None;
1609 None
1610 }
1611 None => None,
1612 };
1613 let completion_position = completion_menu.map(|menu| menu.initial_position);
1614 drop(context_menu);
1615
1616 if effects.completions
1617 && let Some(completion_position) = completion_position
1618 {
1619 let start_offset = selection_start.to_offset(buffer);
1620 let position_matches = start_offset == completion_position.to_offset(buffer);
1621 let continue_showing = if let Some((snap, ..)) =
1622 buffer.point_to_buffer_offset(completion_position)
1623 && !snap.capability.editable()
1624 {
1625 false
1626 } else if position_matches {
1627 if self.snippet_stack.is_empty() {
1628 buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
1629 == Some(CharKind::Word)
1630 } else {
1631 // Snippet choices can be shown even when the cursor is in whitespace.
1632 // Dismissing the menu with actions like backspace is handled by
1633 // invalidation regions.
1634 true
1635 }
1636 } else {
1637 false
1638 };
1639
1640 if continue_showing {
1641 self.open_or_update_completions_menu(None, None, false, window, cx);
1642 } else {
1643 self.hide_context_menu(window, cx);
1644 }
1645 }
1646
1647 hide_hover(self, cx);
1648
1649 self.refresh_code_actions_for_selection(window, cx);
1650 self.refresh_document_highlights(cx);
1651 refresh_linked_ranges(self, window, cx);
1652
1653 self.refresh_selected_text_highlights(&display_map, false, window, cx);
1654 self.refresh_matching_bracket_highlights(&display_map, cx);
1655 self.refresh_outline_symbols_at_cursor(cx);
1656 self.update_visible_edit_prediction(window, cx);
1657 self.hide_blame_popover(true, cx);
1658 if self.git_blame_inline_enabled {
1659 self.start_inline_blame_timer(window, cx);
1660 }
1661 }
1662
1663 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1664
1665 if local && !self.suppress_selection_callback {
1666 if let Some(callback) = self.on_local_selections_changed.as_ref() {
1667 let cursor_position = self.selections.newest::<Point>(&display_map).head();
1668 callback(cursor_position, window, cx);
1669 }
1670 }
1671
1672 cx.emit(EditorEvent::SelectionsChanged { local });
1673
1674 let selections = &self.selections.disjoint_anchors_arc();
1675 if local && let Some(buffer_snapshot) = buffer.as_singleton() {
1676 let inmemory_selections = selections
1677 .iter()
1678 .map(|s| {
1679 let start = s.range().start.text_anchor_in(buffer_snapshot);
1680 let end = s.range().end.text_anchor_in(buffer_snapshot);
1681 (start..end).to_point(buffer_snapshot)
1682 })
1683 .collect();
1684 self.update_restoration_data(cx, |data| {
1685 data.selections = inmemory_selections;
1686 });
1687
1688 if WorkspaceSettings::get(None, cx).restore_on_startup
1689 != RestoreOnStartupBehavior::EmptyTab
1690 && let Some(workspace_id) = self.workspace_serialization_id(cx)
1691 {
1692 let snapshot = self.buffer().read(cx).snapshot(cx);
1693 let selections = selections.clone();
1694 let background_executor = cx.background_executor().clone();
1695 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
1696 let db = EditorDb::global(cx);
1697 self.serialize_selections = cx.background_spawn(async move {
1698 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
1699 let db_selections = selections
1700 .iter()
1701 .map(|selection| {
1702 (
1703 selection.start.to_offset(&snapshot).0,
1704 selection.end.to_offset(&snapshot).0,
1705 )
1706 })
1707 .collect();
1708
1709 db.save_editor_selections(editor_id, workspace_id, db_selections)
1710 .await
1711 .with_context(|| {
1712 format!(
1713 "persisting editor selections for editor {editor_id}, \
1714 workspace {workspace_id:?}"
1715 )
1716 })
1717 .log_err();
1718 });
1719 }
1720 }
1721
1722 cx.notify();
1723 }
1724
1725 fn apply_selection_effects(
1726 &mut self,
1727 state: DeferredSelectionEffectsState,
1728 window: &mut Window,
1729 cx: &mut Context<Self>,
1730 ) {
1731 if state.changed {
1732 self.selection_history.push(state.history_entry);
1733
1734 if let Some(autoscroll) = state.effects.scroll {
1735 self.request_autoscroll(autoscroll, cx);
1736 }
1737
1738 let old_cursor_position = &state.old_cursor_position;
1739
1740 self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
1741
1742 if self.should_open_signature_help_automatically(old_cursor_position, cx) {
1743 self.show_signature_help_auto(window, cx);
1744 }
1745 }
1746 }
1747
1748 fn select_columns(
1749 &mut self,
1750 head: DisplayPoint,
1751 goal_column: u32,
1752 display_map: &DisplaySnapshot,
1753 window: &mut Window,
1754 cx: &mut Context<Self>,
1755 ) {
1756 let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
1757 return;
1758 };
1759
1760 let tail = match columnar_state {
1761 ColumnarSelectionState::FromMouse {
1762 selection_tail,
1763 display_point,
1764 } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
1765 ColumnarSelectionState::FromSelection { selection_tail } => {
1766 selection_tail.to_display_point(display_map)
1767 }
1768 };
1769
1770 let start_row = cmp::min(tail.row(), head.row());
1771 let end_row = cmp::max(tail.row(), head.row());
1772
1773 // Anchor the columnar rectangle in x pixels rather than byte columns so
1774 // rows with multi-byte characters (e.g. diacritics) stay visually aligned.
1775 let text_layout_details = self.text_layout_details(window, cx);
1776
1777 // The mouse handlers encode drags past a line's end as extra columns
1778 // beyond the line length, in em layout widths (see
1779 // `PositionMap::point_for_position`). `x_for_display_point` clamps at
1780 // the line's width, so convert that overshoot back to pixels with the
1781 // same unit to keep the rectangle tracking the mouse past short lines.
1782 let font_id = text_layout_details
1783 .text_system
1784 .resolve_font(&text_layout_details.editor_style.text.font());
1785 let font_size = text_layout_details
1786 .editor_style
1787 .text
1788 .font_size
1789 .to_pixels(text_layout_details.rem_size);
1790 let em_layout_width = text_layout_details
1791 .text_system
1792 .em_layout_width(font_id, font_size);
1793 let x_for_unclipped_point = |point: DisplayPoint| {
1794 let line_len = display_map.line_len(point.row());
1795 if point.column() > line_len {
1796 let eol_x = display_map.x_for_display_point(
1797 DisplayPoint::new(point.row(), line_len),
1798 &text_layout_details,
1799 );
1800 eol_x + em_layout_width * (point.column() - line_len) as f32
1801 } else {
1802 display_map.x_for_display_point(point, &text_layout_details)
1803 }
1804 };
1805
1806 let tail_x = x_for_unclipped_point(tail);
1807 let head_x = x_for_unclipped_point(DisplayPoint::new(head.row(), goal_column));
1808 let start_x = tail_x.min(head_x);
1809 let end_x = tail_x.max(head_x);
1810 let reversed = head_x < tail_x;
1811
1812 let selection_ranges = (start_row.0..=end_row.0)
1813 .map(DisplayRow)
1814 .filter_map(|row| {
1815 if display_map.is_block_line(row) {
1816 return None;
1817 }
1818
1819 let layout = display_map.layout_row(row, &text_layout_details);
1820 if matches!(columnar_state, ColumnarSelectionState::FromSelection { .. })
1821 && start_x > layout.width
1822 {
1823 return None;
1824 }
1825
1826 let start_column = layout.closest_index_for_x(start_x) as u32;
1827 let end_column = layout.closest_index_for_x(end_x) as u32;
1828
1829 let start = display_map
1830 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1831 .to_point(display_map);
1832 let end = display_map
1833 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1834 .to_point(display_map);
1835 if reversed {
1836 Some(end..start)
1837 } else {
1838 Some(start..end)
1839 }
1840 })
1841 .collect::<Vec<_>>();
1842 if selection_ranges.is_empty() {
1843 return;
1844 }
1845
1846 let ranges = match columnar_state {
1847 ColumnarSelectionState::FromMouse { .. } => {
1848 let mut non_empty_ranges = selection_ranges
1849 .iter()
1850 .filter(|selection_range| selection_range.start != selection_range.end)
1851 .peekable();
1852 if non_empty_ranges.peek().is_some() {
1853 non_empty_ranges.cloned().collect()
1854 } else {
1855 selection_ranges
1856 }
1857 }
1858 _ => selection_ranges,
1859 };
1860
1861 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
1862 s.select_ranges(ranges);
1863 });
1864 cx.notify();
1865 }
1866
1867 fn pending_selection_and_mode(&self) -> Option<(Selection<Anchor>, SelectMode)> {
1868 Some((
1869 self.selections.pending_anchor()?.clone(),
1870 self.selections.pending_mode()?,
1871 ))
1872 }
1873
1874 fn add_selection(
1875 &mut self,
1876 above: bool,
1877 skip_soft_wrap: bool,
1878 window: &mut Window,
1879 cx: &mut Context<Self>,
1880 ) {
1881 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1882 let all_selections = self.selections.all::<Point>(&display_map);
1883 let text_layout_details = self.text_layout_details(window, cx);
1884
1885 let (mut columnar_selections, new_selections_to_columnarize) = {
1886 if let Some(state) = self.add_selections_state.as_ref() {
1887 let columnar_selection_ids: HashSet<_> = state
1888 .groups
1889 .iter()
1890 .flat_map(|group| group.stack.iter())
1891 .copied()
1892 .collect();
1893
1894 all_selections
1895 .into_iter()
1896 .partition(|s| columnar_selection_ids.contains(&s.id))
1897 } else {
1898 (Vec::new(), all_selections)
1899 }
1900 };
1901
1902 let mut state = self
1903 .add_selections_state
1904 .take()
1905 .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
1906
1907 for selection in new_selections_to_columnarize {
1908 let range = selection.display_range(&display_map).sorted();
1909 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
1910 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
1911 let positions = start_x.min(end_x)..start_x.max(end_x);
1912 let mut stack = Vec::new();
1913 for row in range.start.row().0..=range.end.row().0 {
1914 if let Some(selection) = self.selections.build_columnar_selection(
1915 &display_map,
1916 DisplayRow(row),
1917 &positions,
1918 selection.reversed,
1919 &text_layout_details,
1920 ) {
1921 stack.push(selection.id);
1922 columnar_selections.push(selection);
1923 }
1924 }
1925 if !stack.is_empty() {
1926 if above {
1927 stack.reverse();
1928 }
1929 state.groups.push(AddSelectionsGroup { above, stack });
1930 }
1931 }
1932
1933 let mut final_selections = Vec::new();
1934 let end_row = if above {
1935 DisplayRow(0)
1936 } else {
1937 display_map.max_point().row()
1938 };
1939
1940 // When `skip_soft_wrap` is true, we place new selections by column rather than
1941 // pixel position, so we need to keep track of the column range of the oldest
1942 // selection in each group, because intermediate selections may have been
1943 // clamped to shorter lines. Columns are counted with tabs expanded so that
1944 // tab-aligned code lines up the way it renders.
1945 let mut goal_columns_by_selection_id = if skip_soft_wrap {
1946 let mut map = HashMap::default();
1947 for group in state.groups.iter() {
1948 if let Some(oldest_id) = group.stack.first() {
1949 if let Some(oldest_selection) =
1950 columnar_selections.iter().find(|s| s.id == *oldest_id)
1951 {
1952 let start_col = display_map.tab_expanded_column(oldest_selection.start);
1953 let end_col = display_map.tab_expanded_column(oldest_selection.end);
1954 let goal_columns = start_col.min(end_col)..start_col.max(end_col);
1955 for id in &group.stack {
1956 map.insert(*id, goal_columns.clone());
1957 }
1958 }
1959 }
1960 }
1961 map
1962 } else {
1963 HashMap::default()
1964 };
1965
1966 let mut last_added_item_per_group = HashMap::default();
1967 for group in state.groups.iter_mut() {
1968 if let Some(last_id) = group.stack.last() {
1969 last_added_item_per_group.insert(*last_id, group);
1970 }
1971 }
1972
1973 for selection in columnar_selections {
1974 if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
1975 if above == group.above {
1976 let range = selection.display_range(&display_map).sorted();
1977 debug_assert_eq!(range.start.row(), range.end.row());
1978 let row = range.start.row();
1979 let positions =
1980 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
1981 Pixels::from(start)..Pixels::from(end)
1982 } else {
1983 let start_x =
1984 display_map.x_for_display_point(range.start, &text_layout_details);
1985 let end_x =
1986 display_map.x_for_display_point(range.end, &text_layout_details);
1987 start_x.min(end_x)..start_x.max(end_x)
1988 };
1989
1990 let maybe_new_selection = if skip_soft_wrap {
1991 let goal_columns = goal_columns_by_selection_id
1992 .remove(&selection.id)
1993 .unwrap_or_else(|| {
1994 let start_col = display_map.tab_expanded_column(selection.start);
1995 let end_col = display_map.tab_expanded_column(selection.end);
1996 start_col.min(end_col)..start_col.max(end_col)
1997 });
1998 self.selections.find_next_columnar_selection_by_buffer_row(
1999 &display_map,
2000 row,
2001 end_row,
2002 above,
2003 &goal_columns,
2004 selection.reversed,
2005 &text_layout_details,
2006 )
2007 } else {
2008 self.selections.find_next_columnar_selection_by_display_row(
2009 &display_map,
2010 row,
2011 end_row,
2012 above,
2013 &positions,
2014 selection.reversed,
2015 &text_layout_details,
2016 )
2017 };
2018
2019 if let Some(new_selection) = maybe_new_selection {
2020 group.stack.push(new_selection.id);
2021 if above {
2022 final_selections.push(new_selection);
2023 final_selections.push(selection);
2024 } else {
2025 final_selections.push(selection);
2026 final_selections.push(new_selection);
2027 }
2028 } else {
2029 final_selections.push(selection);
2030 }
2031 } else {
2032 group.stack.pop();
2033 }
2034 } else {
2035 final_selections.push(selection);
2036 }
2037 }
2038
2039 self.change_selections(Default::default(), window, cx, |s| {
2040 s.select(final_selections);
2041 });
2042
2043 let final_selection_ids: HashSet<_> = self
2044 .selections
2045 .all::<Point>(&display_map)
2046 .iter()
2047 .map(|s| s.id)
2048 .collect();
2049 state.groups.retain_mut(|group| {
2050 // selections might get merged above so we remove invalid items from stacks
2051 group.stack.retain(|id| final_selection_ids.contains(id));
2052
2053 // single selection in stack can be treated as initial state
2054 group.stack.len() > 1
2055 });
2056
2057 if !state.groups.is_empty() {
2058 self.add_selections_state = Some(state);
2059 }
2060 }
2061
2062 fn select_match_ranges(
2063 &mut self,
2064 range: Range<MultiBufferOffset>,
2065 reversed: bool,
2066 replace_newest: bool,
2067 auto_scroll: Option<Autoscroll>,
2068 window: &mut Window,
2069 cx: &mut Context<Editor>,
2070 ) {
2071 self.unfold_ranges(
2072 std::slice::from_ref(&range),
2073 false,
2074 auto_scroll.is_some(),
2075 cx,
2076 );
2077 let effects = if let Some(scroll) = auto_scroll {
2078 SelectionEffects::scroll(scroll)
2079 } else {
2080 SelectionEffects::no_scroll()
2081 };
2082 self.change_selections(effects, window, cx, |s| {
2083 if replace_newest {
2084 s.delete(s.newest_anchor().id);
2085 }
2086 if reversed {
2087 s.insert_range(range.end..range.start);
2088 } else {
2089 s.insert_range(range);
2090 }
2091 });
2092 }
2093
2094 fn select_next_match_internal(
2095 &mut self,
2096 display_map: &DisplaySnapshot,
2097 replace_newest: bool,
2098 autoscroll: Option<Autoscroll>,
2099 window: &mut Window,
2100 cx: &mut Context<Self>,
2101 ) -> Result<()> {
2102 let buffer = display_map.buffer_snapshot();
2103 let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
2104 if let Some(mut select_next_state) = self.select_next_state.take() {
2105 let query = &select_next_state.query;
2106 if !select_next_state.done {
2107 let first_selection = selections
2108 .iter()
2109 .min_by_key(|s| s.id)
2110 .context("missing selection for select next action")?;
2111 let last_selection = selections
2112 .iter()
2113 .max_by_key(|s| s.id)
2114 .context("missing selection for select next action")?;
2115 let mut next_selected_range = None;
2116
2117 let bytes_after_last_selection =
2118 buffer.bytes_in_range(last_selection.end..buffer.len());
2119 let bytes_before_first_selection =
2120 buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
2121 let query_matches = query
2122 .stream_find_iter(bytes_after_last_selection)
2123 .map(|result| (last_selection.end, result))
2124 .chain(
2125 query
2126 .stream_find_iter(bytes_before_first_selection)
2127 .map(|result| (MultiBufferOffset(0), result)),
2128 );
2129
2130 for (start_offset, query_match) in query_matches {
2131 let query_match = query_match.context("query match for select next action")?;
2132 let offset_range =
2133 start_offset + query_match.start()..start_offset + query_match.end();
2134
2135 if !select_next_state.wordwise
2136 || (!buffer.is_inside_word(offset_range.start, None)
2137 && !buffer.is_inside_word(offset_range.end, None))
2138 {
2139 let idx = selections
2140 .partition_point(|selection| selection.end <= offset_range.start);
2141 let overlaps = selections
2142 .get(idx)
2143 .map_or(false, |selection| selection.start < offset_range.end);
2144
2145 if !overlaps {
2146 next_selected_range = Some(offset_range);
2147 break;
2148 }
2149 }
2150 }
2151
2152 if let Some(next_selected_range) = next_selected_range {
2153 self.select_match_ranges(
2154 next_selected_range,
2155 last_selection.reversed,
2156 replace_newest,
2157 autoscroll,
2158 window,
2159 cx,
2160 );
2161 } else {
2162 select_next_state.done = true;
2163 }
2164 }
2165
2166 self.select_next_state = Some(select_next_state);
2167 } else {
2168 let mut only_carets = true;
2169 let mut same_text_selected = true;
2170 let mut selected_text = None;
2171
2172 let mut selections_iter = selections.iter().peekable();
2173 while let Some(selection) = selections_iter.next() {
2174 if selection.start != selection.end {
2175 only_carets = false;
2176 }
2177
2178 if same_text_selected {
2179 if selected_text.is_none() {
2180 selected_text =
2181 Some(buffer.text_for_range(selection.range()).collect::<String>());
2182 }
2183
2184 if let Some(next_selection) = selections_iter.peek() {
2185 if next_selection.len() == selection.len() {
2186 let next_selected_text = buffer
2187 .text_for_range(next_selection.range())
2188 .collect::<String>();
2189 if Some(next_selected_text) != selected_text {
2190 same_text_selected = false;
2191 selected_text = None;
2192 }
2193 } else {
2194 same_text_selected = false;
2195 selected_text = None;
2196 }
2197 }
2198 }
2199 }
2200
2201 if only_carets {
2202 for selection in &mut selections {
2203 let (word_range, _) = buffer.surrounding_word(selection.start, None);
2204 selection.start = word_range.start;
2205 selection.end = word_range.end;
2206 selection.goal = SelectionGoal::None;
2207 selection.reversed = false;
2208 self.select_match_ranges(
2209 selection.start..selection.end,
2210 selection.reversed,
2211 replace_newest,
2212 autoscroll,
2213 window,
2214 cx,
2215 );
2216 }
2217
2218 if selections.len() == 1 {
2219 let selection = selections
2220 .last()
2221 .expect("ensured that there's only one selection");
2222 let query = buffer
2223 .text_for_range(selection.start..selection.end)
2224 .collect::<String>();
2225 let is_empty = query.is_empty();
2226 let select_state = SelectNextState {
2227 query: self.build_query(&[query], cx)?,
2228 wordwise: true,
2229 done: is_empty,
2230 };
2231 self.select_next_state = Some(select_state);
2232 } else {
2233 self.select_next_state = None;
2234 }
2235 } else if let Some(selected_text) = selected_text {
2236 self.select_next_state = Some(SelectNextState {
2237 query: self.build_query(&[selected_text], cx)?,
2238 wordwise: false,
2239 done: false,
2240 });
2241 self.select_next_match_internal(
2242 display_map,
2243 replace_newest,
2244 autoscroll,
2245 window,
2246 cx,
2247 )?;
2248 }
2249 }
2250 Ok(())
2251 }
2252
2253 fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
2254 where
2255 I: IntoIterator<Item = P>,
2256 P: AsRef<[u8]>,
2257 {
2258 let case_sensitive = self
2259 .select_next_is_case_sensitive
2260 .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
2261
2262 let mut builder = AhoCorasickBuilder::new();
2263 builder.ascii_case_insensitive(!case_sensitive);
2264 builder.build(patterns)
2265 }
2266
2267 fn find_syntax_node_boundary(
2268 &self,
2269 selection_pos: MultiBufferOffset,
2270 move_to_end: bool,
2271 display_map: &DisplaySnapshot,
2272 buffer: &MultiBufferSnapshot,
2273 ) -> MultiBufferOffset {
2274 let old_range = selection_pos..selection_pos;
2275 let mut new_pos = selection_pos;
2276 let mut search_range = old_range;
2277 while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
2278 search_range = range.clone();
2279 if !node.is_named()
2280 || display_map.intersects_fold(range.start)
2281 || display_map.intersects_fold(range.end)
2282 // If cursor is already at the end of the syntax node, continue searching
2283 || (move_to_end && range.end == selection_pos)
2284 // If cursor is already at the start of the syntax node, continue searching
2285 || (!move_to_end && range.start == selection_pos)
2286 {
2287 continue;
2288 }
2289
2290 // If we found a string_content node, find the largest parent that is still string_content
2291 // Enables us to skip to the end of strings without taking multiple steps inside the string
2292 let (_, final_range) = if node.kind() == "string_content" {
2293 let mut current_node = node;
2294 let mut current_range = range;
2295 while let Some((parent, parent_range)) =
2296 buffer.syntax_ancestor(current_range.clone())
2297 {
2298 if parent.kind() == "string_content" {
2299 current_node = parent;
2300 current_range = parent_range;
2301 } else {
2302 break;
2303 }
2304 }
2305
2306 (current_node, current_range)
2307 } else {
2308 (node, range)
2309 };
2310
2311 new_pos = if move_to_end {
2312 final_range.end
2313 } else {
2314 final_range.start
2315 };
2316
2317 break;
2318 }
2319
2320 new_pos
2321 }
2322
2323 fn move_cursors_to_syntax_nodes(
2324 &mut self,
2325 window: &mut Window,
2326 cx: &mut Context<Self>,
2327 move_to_end: bool,
2328 ) {
2329 let old_selections: Box<[_]> = self
2330 .selections
2331 .all::<MultiBufferOffset>(&self.display_snapshot(cx))
2332 .into();
2333 if old_selections.is_empty() {
2334 return;
2335 }
2336
2337 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2338 let buffer = self.buffer.read(cx).snapshot(cx);
2339
2340 let new_selections = old_selections
2341 .iter()
2342 .map(|selection| {
2343 if !selection.is_empty() {
2344 return selection.clone();
2345 }
2346
2347 let selection_pos = selection.head();
2348 let new_pos = self.find_syntax_node_boundary(
2349 selection_pos,
2350 move_to_end,
2351 &display_map,
2352 &buffer,
2353 );
2354
2355 Selection {
2356 id: selection.id,
2357 start: new_pos,
2358 end: new_pos,
2359 goal: SelectionGoal::None,
2360 reversed: false,
2361 }
2362 })
2363 .collect::<Vec<_>>();
2364
2365 self.change_selections(Default::default(), window, cx, |s| {
2366 s.select(new_selections);
2367 });
2368 self.request_autoscroll(Autoscroll::newest(), cx);
2369 }
2370
2371 fn select_to_syntax_nodes(
2372 &mut self,
2373 window: &mut Window,
2374 cx: &mut Context<Self>,
2375 move_to_end: bool,
2376 ) {
2377 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2378 let buffer = self.buffer.read(cx).snapshot(cx);
2379 let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
2380
2381 let new_selections = old_selections
2382 .iter()
2383 .map(|selection| {
2384 let new_pos = self.find_syntax_node_boundary(
2385 selection.head(),
2386 move_to_end,
2387 &display_map,
2388 &buffer,
2389 );
2390
2391 let mut new_selection = selection.clone();
2392 new_selection.set_head(new_pos, SelectionGoal::None);
2393 new_selection
2394 })
2395 .collect::<Vec<_>>();
2396
2397 self.change_selections(Default::default(), window, cx, |s| {
2398 s.select(new_selections);
2399 });
2400 }
2401}
2402