1pub mod display_map;
2mod element;
3pub mod items;
4pub mod movement;
5mod multi_buffer;
6
7#[cfg(test)]
8mod test;
9
10use aho_corasick::AhoCorasick;
11use anyhow::Result;
12use clock::ReplicaId;
13use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
14pub use display_map::DisplayPoint;
15use display_map::*;
16pub use element::*;
17use fuzzy::{StringMatch, StringMatchCandidate};
18use gpui::{
19 actions,
20 color::Color,
21 elements::*,
22 executor,
23 fonts::{self, HighlightStyle, TextStyle},
24 geometry::vector::{vec2f, Vector2F},
25 impl_actions, impl_internal_actions,
26 platform::CursorStyle,
27 text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
28 ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
29 WeakViewHandle,
30};
31use itertools::Itertools as _;
32pub use language::{char_kind, CharKind};
33use language::{
34 BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity,
35 Language, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
36};
37use multi_buffer::MultiBufferChunks;
38pub use multi_buffer::{
39 Anchor, AnchorRangeExt, ExcerptId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
40};
41use ordered_float::OrderedFloat;
42use project::{Project, ProjectTransaction};
43use serde::{Deserialize, Serialize};
44use settings::Settings;
45use smallvec::SmallVec;
46use smol::Timer;
47use snippet::Snippet;
48use std::{
49 any::TypeId,
50 borrow::Cow,
51 cmp::{self, Ordering, Reverse},
52 iter::{self, FromIterator},
53 mem,
54 ops::{Deref, DerefMut, Range, RangeInclusive, Sub},
55 sync::Arc,
56 time::{Duration, Instant},
57};
58pub use sum_tree::Bias;
59use text::rope::TextDimension;
60use theme::{DiagnosticStyle, Theme};
61use util::{post_inc, ResultExt, TryFutureExt};
62use workspace::{ItemNavHistory, Workspace};
63
64const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
65const MAX_LINE_LEN: usize = 1024;
66const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
67const MAX_SELECTION_HISTORY_LEN: usize = 1024;
68
69#[derive(Clone, Deserialize)]
70pub struct SelectNext {
71 #[serde(default)]
72 pub replace_newest: bool,
73}
74
75#[derive(Clone)]
76pub struct GoToDiagnostic(pub Direction);
77
78#[derive(Clone)]
79pub struct Scroll(pub Vector2F);
80
81#[derive(Clone)]
82pub struct Select(pub SelectPhase);
83
84#[derive(Clone, Deserialize)]
85pub struct Input(pub String);
86
87#[derive(Clone, Deserialize)]
88pub struct SelectToBeginningOfLine {
89 #[serde(default)]
90 stop_at_soft_wraps: bool,
91}
92
93#[derive(Clone, Deserialize)]
94pub struct SelectToEndOfLine {
95 #[serde(default)]
96 stop_at_soft_wraps: bool,
97}
98
99#[derive(Clone, Deserialize)]
100pub struct ToggleCodeActions {
101 #[serde(default)]
102 pub deployed_from_indicator: bool,
103}
104
105#[derive(Clone, Default, Deserialize)]
106pub struct ConfirmCompletion {
107 #[serde(default)]
108 pub item_ix: Option<usize>,
109}
110
111#[derive(Clone, Default, Deserialize)]
112pub struct ConfirmCodeAction {
113 #[serde(default)]
114 pub item_ix: Option<usize>,
115}
116
117actions!(
118 editor,
119 [
120 Cancel,
121 Backspace,
122 Delete,
123 Newline,
124 GoToNextDiagnostic,
125 GoToPrevDiagnostic,
126 Indent,
127 Outdent,
128 DeleteLine,
129 DeleteToPreviousWordStart,
130 DeleteToPreviousSubwordStart,
131 DeleteToNextWordEnd,
132 DeleteToNextSubwordEnd,
133 DeleteToBeginningOfLine,
134 DeleteToEndOfLine,
135 CutToEndOfLine,
136 DuplicateLine,
137 MoveLineUp,
138 MoveLineDown,
139 Transpose,
140 Cut,
141 Copy,
142 Paste,
143 Undo,
144 Redo,
145 MoveUp,
146 MoveDown,
147 MoveLeft,
148 MoveRight,
149 MoveToPreviousWordStart,
150 MoveToPreviousSubwordStart,
151 MoveToNextWordEnd,
152 MoveToNextSubwordEnd,
153 MoveToBeginningOfLine,
154 MoveToEndOfLine,
155 MoveToBeginning,
156 MoveToEnd,
157 SelectUp,
158 SelectDown,
159 SelectLeft,
160 SelectRight,
161 SelectToPreviousWordStart,
162 SelectToPreviousSubwordStart,
163 SelectToNextWordEnd,
164 SelectToNextSubwordEnd,
165 SelectToBeginning,
166 SelectToEnd,
167 SelectAll,
168 SelectLine,
169 SplitSelectionIntoLines,
170 AddSelectionAbove,
171 AddSelectionBelow,
172 Tab,
173 TabPrev,
174 ToggleComments,
175 SelectLargerSyntaxNode,
176 SelectSmallerSyntaxNode,
177 MoveToEnclosingBracket,
178 UndoSelection,
179 RedoSelection,
180 GoToDefinition,
181 FindAllReferences,
182 Rename,
183 ConfirmRename,
184 PageUp,
185 PageDown,
186 Fold,
187 UnfoldLines,
188 FoldSelectedRanges,
189 ShowCompletions,
190 OpenExcerpts,
191 RestartLanguageServer,
192 ]
193);
194
195impl_actions!(
196 editor,
197 [
198 Input,
199 SelectNext,
200 SelectToBeginningOfLine,
201 SelectToEndOfLine,
202 ToggleCodeActions,
203 ConfirmCompletion,
204 ConfirmCodeAction,
205 ]
206);
207
208impl_internal_actions!(editor, [Scroll, Select]);
209
210enum DocumentHighlightRead {}
211enum DocumentHighlightWrite {}
212
213#[derive(Copy, Clone, PartialEq, Eq)]
214pub enum Direction {
215 Prev,
216 Next,
217}
218
219pub fn init(cx: &mut MutableAppContext) {
220 cx.add_action(Editor::open_new);
221 cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
222 cx.add_action(Editor::select);
223 cx.add_action(Editor::cancel);
224 cx.add_action(Editor::handle_input);
225 cx.add_action(Editor::newline);
226 cx.add_action(Editor::backspace);
227 cx.add_action(Editor::delete);
228 cx.add_action(Editor::tab);
229 cx.add_action(Editor::tab_prev);
230 cx.add_action(Editor::indent);
231 cx.add_action(Editor::outdent);
232 cx.add_action(Editor::delete_line);
233 cx.add_action(Editor::delete_to_previous_word_start);
234 cx.add_action(Editor::delete_to_previous_subword_start);
235 cx.add_action(Editor::delete_to_next_word_end);
236 cx.add_action(Editor::delete_to_next_subword_end);
237 cx.add_action(Editor::delete_to_beginning_of_line);
238 cx.add_action(Editor::delete_to_end_of_line);
239 cx.add_action(Editor::cut_to_end_of_line);
240 cx.add_action(Editor::duplicate_line);
241 cx.add_action(Editor::move_line_up);
242 cx.add_action(Editor::move_line_down);
243 cx.add_action(Editor::transpose);
244 cx.add_action(Editor::cut);
245 cx.add_action(Editor::copy);
246 cx.add_action(Editor::paste);
247 cx.add_action(Editor::undo);
248 cx.add_action(Editor::redo);
249 cx.add_action(Editor::move_up);
250 cx.add_action(Editor::move_down);
251 cx.add_action(Editor::move_left);
252 cx.add_action(Editor::move_right);
253 cx.add_action(Editor::move_to_previous_word_start);
254 cx.add_action(Editor::move_to_previous_subword_start);
255 cx.add_action(Editor::move_to_next_word_end);
256 cx.add_action(Editor::move_to_next_subword_end);
257 cx.add_action(Editor::move_to_beginning_of_line);
258 cx.add_action(Editor::move_to_end_of_line);
259 cx.add_action(Editor::move_to_beginning);
260 cx.add_action(Editor::move_to_end);
261 cx.add_action(Editor::select_up);
262 cx.add_action(Editor::select_down);
263 cx.add_action(Editor::select_left);
264 cx.add_action(Editor::select_right);
265 cx.add_action(Editor::select_to_previous_word_start);
266 cx.add_action(Editor::select_to_previous_subword_start);
267 cx.add_action(Editor::select_to_next_word_end);
268 cx.add_action(Editor::select_to_next_subword_end);
269 cx.add_action(Editor::select_to_beginning_of_line);
270 cx.add_action(Editor::select_to_end_of_line);
271 cx.add_action(Editor::select_to_beginning);
272 cx.add_action(Editor::select_to_end);
273 cx.add_action(Editor::select_all);
274 cx.add_action(Editor::select_line);
275 cx.add_action(Editor::split_selection_into_lines);
276 cx.add_action(Editor::add_selection_above);
277 cx.add_action(Editor::add_selection_below);
278 cx.add_action(Editor::select_next);
279 cx.add_action(Editor::toggle_comments);
280 cx.add_action(Editor::select_larger_syntax_node);
281 cx.add_action(Editor::select_smaller_syntax_node);
282 cx.add_action(Editor::move_to_enclosing_bracket);
283 cx.add_action(Editor::undo_selection);
284 cx.add_action(Editor::redo_selection);
285 cx.add_action(Editor::go_to_next_diagnostic);
286 cx.add_action(Editor::go_to_prev_diagnostic);
287 cx.add_action(Editor::go_to_definition);
288 cx.add_action(Editor::page_up);
289 cx.add_action(Editor::page_down);
290 cx.add_action(Editor::fold);
291 cx.add_action(Editor::unfold_lines);
292 cx.add_action(Editor::fold_selected_ranges);
293 cx.add_action(Editor::show_completions);
294 cx.add_action(Editor::toggle_code_actions);
295 cx.add_action(Editor::open_excerpts);
296 cx.add_action(Editor::restart_language_server);
297 cx.add_async_action(Editor::confirm_completion);
298 cx.add_async_action(Editor::confirm_code_action);
299 cx.add_async_action(Editor::rename);
300 cx.add_async_action(Editor::confirm_rename);
301 cx.add_async_action(Editor::find_all_references);
302
303 workspace::register_project_item::<Editor>(cx);
304 workspace::register_followable_item::<Editor>(cx);
305}
306
307trait InvalidationRegion {
308 fn ranges(&self) -> &[Range<Anchor>];
309}
310
311#[derive(Clone, Debug)]
312pub enum SelectPhase {
313 Begin {
314 position: DisplayPoint,
315 add: bool,
316 click_count: usize,
317 },
318 BeginColumnar {
319 position: DisplayPoint,
320 overshoot: u32,
321 },
322 Extend {
323 position: DisplayPoint,
324 click_count: usize,
325 },
326 Update {
327 position: DisplayPoint,
328 overshoot: u32,
329 scroll_position: Vector2F,
330 },
331 End,
332}
333
334#[derive(Clone, Debug)]
335pub enum SelectMode {
336 Character,
337 Word(Range<Anchor>),
338 Line(Range<Anchor>),
339 All,
340}
341
342#[derive(PartialEq, Eq)]
343pub enum Autoscroll {
344 Fit,
345 Center,
346 Newest,
347}
348
349#[derive(Copy, Clone, PartialEq, Eq)]
350pub enum EditorMode {
351 SingleLine,
352 AutoHeight { max_lines: usize },
353 Full,
354}
355
356#[derive(Clone)]
357pub enum SoftWrap {
358 None,
359 EditorWidth,
360 Column(u32),
361}
362
363#[derive(Clone)]
364pub struct EditorStyle {
365 pub text: TextStyle,
366 pub placeholder_text: Option<TextStyle>,
367 pub theme: theme::Editor,
368}
369
370type CompletionId = usize;
371
372pub type GetFieldEditorTheme = fn(&theme::Theme) -> theme::FieldEditor;
373
374type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
375
376pub struct Editor {
377 handle: WeakViewHandle<Self>,
378 buffer: ModelHandle<MultiBuffer>,
379 display_map: ModelHandle<DisplayMap>,
380 next_selection_id: usize,
381 selections: Arc<[Selection<Anchor>]>,
382 pending_selection: Option<PendingSelection>,
383 columnar_selection_tail: Option<Anchor>,
384 add_selections_state: Option<AddSelectionsState>,
385 select_next_state: Option<SelectNextState>,
386 selection_history: SelectionHistory,
387 autoclose_stack: InvalidationStack<BracketPairState>,
388 snippet_stack: InvalidationStack<SnippetState>,
389 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
390 active_diagnostics: Option<ActiveDiagnosticGroup>,
391 scroll_position: Vector2F,
392 scroll_top_anchor: Anchor,
393 autoscroll_request: Option<(Autoscroll, bool)>,
394 soft_wrap_mode_override: Option<settings::SoftWrap>,
395 get_field_editor_theme: Option<GetFieldEditorTheme>,
396 override_text_style: Option<Box<OverrideTextStyle>>,
397 project: Option<ModelHandle<Project>>,
398 focused: bool,
399 show_local_cursors: bool,
400 show_local_selections: bool,
401 blink_epoch: usize,
402 blinking_paused: bool,
403 mode: EditorMode,
404 vertical_scroll_margin: f32,
405 placeholder_text: Option<Arc<str>>,
406 highlighted_rows: Option<Range<u32>>,
407 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
408 nav_history: Option<ItemNavHistory>,
409 context_menu: Option<ContextMenu>,
410 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
411 next_completion_id: CompletionId,
412 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
413 code_actions_task: Option<Task<()>>,
414 document_highlights_task: Option<Task<()>>,
415 pending_rename: Option<RenameState>,
416 searchable: bool,
417 cursor_shape: CursorShape,
418 keymap_context_layers: BTreeMap<TypeId, gpui::keymap::Context>,
419 input_enabled: bool,
420 leader_replica_id: Option<u16>,
421}
422
423pub struct EditorSnapshot {
424 pub mode: EditorMode,
425 pub display_snapshot: DisplaySnapshot,
426 pub placeholder_text: Option<Arc<str>>,
427 is_focused: bool,
428 scroll_position: Vector2F,
429 scroll_top_anchor: Anchor,
430}
431
432#[derive(Clone)]
433pub struct PendingSelection {
434 selection: Selection<Anchor>,
435 mode: SelectMode,
436}
437
438#[derive(Clone)]
439struct SelectionHistoryEntry {
440 selections: Arc<[Selection<Anchor>]>,
441 select_next_state: Option<SelectNextState>,
442 add_selections_state: Option<AddSelectionsState>,
443}
444
445enum SelectionHistoryMode {
446 Normal,
447 Undoing,
448 Redoing,
449}
450
451impl Default for SelectionHistoryMode {
452 fn default() -> Self {
453 Self::Normal
454 }
455}
456
457#[derive(Default)]
458struct SelectionHistory {
459 selections_by_transaction:
460 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
461 mode: SelectionHistoryMode,
462 undo_stack: VecDeque<SelectionHistoryEntry>,
463 redo_stack: VecDeque<SelectionHistoryEntry>,
464}
465
466impl SelectionHistory {
467 fn insert_transaction(
468 &mut self,
469 transaction_id: TransactionId,
470 selections: Arc<[Selection<Anchor>]>,
471 ) {
472 self.selections_by_transaction
473 .insert(transaction_id, (selections, None));
474 }
475
476 fn transaction(
477 &self,
478 transaction_id: TransactionId,
479 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
480 self.selections_by_transaction.get(&transaction_id)
481 }
482
483 fn transaction_mut(
484 &mut self,
485 transaction_id: TransactionId,
486 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
487 self.selections_by_transaction.get_mut(&transaction_id)
488 }
489
490 fn push(&mut self, entry: SelectionHistoryEntry) {
491 if !entry.selections.is_empty() {
492 match self.mode {
493 SelectionHistoryMode::Normal => {
494 self.push_undo(entry);
495 self.redo_stack.clear();
496 }
497 SelectionHistoryMode::Undoing => self.push_redo(entry),
498 SelectionHistoryMode::Redoing => self.push_undo(entry),
499 }
500 }
501 }
502
503 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
504 if self
505 .undo_stack
506 .back()
507 .map_or(true, |e| e.selections != entry.selections)
508 {
509 self.undo_stack.push_back(entry);
510 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
511 self.undo_stack.pop_front();
512 }
513 }
514 }
515
516 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
517 if self
518 .redo_stack
519 .back()
520 .map_or(true, |e| e.selections != entry.selections)
521 {
522 self.redo_stack.push_back(entry);
523 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
524 self.redo_stack.pop_front();
525 }
526 }
527 }
528}
529
530#[derive(Clone)]
531struct AddSelectionsState {
532 above: bool,
533 stack: Vec<usize>,
534}
535
536#[derive(Clone)]
537struct SelectNextState {
538 query: AhoCorasick,
539 wordwise: bool,
540 done: bool,
541}
542
543struct BracketPairState {
544 ranges: Vec<Range<Anchor>>,
545 pair: BracketPair,
546}
547
548struct SnippetState {
549 ranges: Vec<Vec<Range<Anchor>>>,
550 active_index: usize,
551}
552
553pub struct RenameState {
554 pub range: Range<Anchor>,
555 pub old_name: Arc<str>,
556 pub editor: ViewHandle<Editor>,
557 block_id: BlockId,
558}
559
560struct InvalidationStack<T>(Vec<T>);
561
562enum ContextMenu {
563 Completions(CompletionsMenu),
564 CodeActions(CodeActionsMenu),
565}
566
567impl ContextMenu {
568 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
569 if self.visible() {
570 match self {
571 ContextMenu::Completions(menu) => menu.select_prev(cx),
572 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
573 }
574 true
575 } else {
576 false
577 }
578 }
579
580 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
581 if self.visible() {
582 match self {
583 ContextMenu::Completions(menu) => menu.select_next(cx),
584 ContextMenu::CodeActions(menu) => menu.select_next(cx),
585 }
586 true
587 } else {
588 false
589 }
590 }
591
592 fn visible(&self) -> bool {
593 match self {
594 ContextMenu::Completions(menu) => menu.visible(),
595 ContextMenu::CodeActions(menu) => menu.visible(),
596 }
597 }
598
599 fn render(
600 &self,
601 cursor_position: DisplayPoint,
602 style: EditorStyle,
603 cx: &AppContext,
604 ) -> (DisplayPoint, ElementBox) {
605 match self {
606 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
607 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style),
608 }
609 }
610}
611
612struct CompletionsMenu {
613 id: CompletionId,
614 initial_position: Anchor,
615 buffer: ModelHandle<Buffer>,
616 completions: Arc<[Completion]>,
617 match_candidates: Vec<StringMatchCandidate>,
618 matches: Arc<[StringMatch]>,
619 selected_item: usize,
620 list: UniformListState,
621}
622
623impl CompletionsMenu {
624 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
625 if self.selected_item > 0 {
626 self.selected_item -= 1;
627 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
628 }
629 cx.notify();
630 }
631
632 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
633 if self.selected_item + 1 < self.matches.len() {
634 self.selected_item += 1;
635 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
636 }
637 cx.notify();
638 }
639
640 fn visible(&self) -> bool {
641 !self.matches.is_empty()
642 }
643
644 fn render(&self, style: EditorStyle, _: &AppContext) -> ElementBox {
645 enum CompletionTag {}
646
647 let completions = self.completions.clone();
648 let matches = self.matches.clone();
649 let selected_item = self.selected_item;
650 let container_style = style.autocomplete.container;
651 UniformList::new(self.list.clone(), matches.len(), move |range, items, cx| {
652 let start_ix = range.start;
653 for (ix, mat) in matches[range].iter().enumerate() {
654 let completion = &completions[mat.candidate_id];
655 let item_ix = start_ix + ix;
656 items.push(
657 MouseEventHandler::new::<CompletionTag, _, _>(
658 mat.candidate_id,
659 cx,
660 |state, _| {
661 let item_style = if item_ix == selected_item {
662 style.autocomplete.selected_item
663 } else if state.hovered {
664 style.autocomplete.hovered_item
665 } else {
666 style.autocomplete.item
667 };
668
669 Text::new(completion.label.text.clone(), style.text.clone())
670 .with_soft_wrap(false)
671 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
672 &completion.label.text,
673 style.text.color.into(),
674 styled_runs_for_code_label(&completion.label, &style.syntax),
675 &mat.positions,
676 ))
677 .contained()
678 .with_style(item_style)
679 .boxed()
680 },
681 )
682 .with_cursor_style(CursorStyle::PointingHand)
683 .on_mouse_down(move |cx| {
684 cx.dispatch_action(ConfirmCompletion {
685 item_ix: Some(item_ix),
686 });
687 })
688 .boxed(),
689 );
690 }
691 })
692 .with_width_from_item(
693 self.matches
694 .iter()
695 .enumerate()
696 .max_by_key(|(_, mat)| {
697 self.completions[mat.candidate_id]
698 .label
699 .text
700 .chars()
701 .count()
702 })
703 .map(|(ix, _)| ix),
704 )
705 .contained()
706 .with_style(container_style)
707 .boxed()
708 }
709
710 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
711 let mut matches = if let Some(query) = query {
712 fuzzy::match_strings(
713 &self.match_candidates,
714 query,
715 false,
716 100,
717 &Default::default(),
718 executor,
719 )
720 .await
721 } else {
722 self.match_candidates
723 .iter()
724 .enumerate()
725 .map(|(candidate_id, candidate)| StringMatch {
726 candidate_id,
727 score: Default::default(),
728 positions: Default::default(),
729 string: candidate.string.clone(),
730 })
731 .collect()
732 };
733 matches.sort_unstable_by_key(|mat| {
734 (
735 Reverse(OrderedFloat(mat.score)),
736 self.completions[mat.candidate_id].sort_key(),
737 )
738 });
739
740 for mat in &mut matches {
741 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
742 for position in &mut mat.positions {
743 *position += filter_start;
744 }
745 }
746
747 self.matches = matches.into();
748 }
749}
750
751#[derive(Clone)]
752struct CodeActionsMenu {
753 actions: Arc<[CodeAction]>,
754 buffer: ModelHandle<Buffer>,
755 selected_item: usize,
756 list: UniformListState,
757 deployed_from_indicator: bool,
758}
759
760impl CodeActionsMenu {
761 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
762 if self.selected_item > 0 {
763 self.selected_item -= 1;
764 cx.notify()
765 }
766 }
767
768 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
769 if self.selected_item + 1 < self.actions.len() {
770 self.selected_item += 1;
771 cx.notify()
772 }
773 }
774
775 fn visible(&self) -> bool {
776 !self.actions.is_empty()
777 }
778
779 fn render(
780 &self,
781 mut cursor_position: DisplayPoint,
782 style: EditorStyle,
783 ) -> (DisplayPoint, ElementBox) {
784 enum ActionTag {}
785
786 let container_style = style.autocomplete.container;
787 let actions = self.actions.clone();
788 let selected_item = self.selected_item;
789 let element =
790 UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
791 let start_ix = range.start;
792 for (ix, action) in actions[range].iter().enumerate() {
793 let item_ix = start_ix + ix;
794 items.push(
795 MouseEventHandler::new::<ActionTag, _, _>(item_ix, cx, |state, _| {
796 let item_style = if item_ix == selected_item {
797 style.autocomplete.selected_item
798 } else if state.hovered {
799 style.autocomplete.hovered_item
800 } else {
801 style.autocomplete.item
802 };
803
804 Text::new(action.lsp_action.title.clone(), style.text.clone())
805 .with_soft_wrap(false)
806 .contained()
807 .with_style(item_style)
808 .boxed()
809 })
810 .with_cursor_style(CursorStyle::PointingHand)
811 .on_mouse_down(move |cx| {
812 cx.dispatch_action(ConfirmCodeAction {
813 item_ix: Some(item_ix),
814 });
815 })
816 .boxed(),
817 );
818 }
819 })
820 .with_width_from_item(
821 self.actions
822 .iter()
823 .enumerate()
824 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
825 .map(|(ix, _)| ix),
826 )
827 .contained()
828 .with_style(container_style)
829 .boxed();
830
831 if self.deployed_from_indicator {
832 *cursor_position.column_mut() = 0;
833 }
834
835 (cursor_position, element)
836 }
837}
838
839#[derive(Debug)]
840struct ActiveDiagnosticGroup {
841 primary_range: Range<Anchor>,
842 primary_message: String,
843 blocks: HashMap<BlockId, Diagnostic>,
844 is_valid: bool,
845}
846
847#[derive(Serialize, Deserialize)]
848struct ClipboardSelection {
849 len: usize,
850 is_entire_line: bool,
851}
852
853#[derive(Debug)]
854pub struct NavigationData {
855 // Matching offsets for anchor and scroll_top_anchor allows us to recreate the anchor if the buffer
856 // has since been closed
857 cursor_anchor: Anchor,
858 cursor_position: Point,
859 scroll_position: Vector2F,
860 scroll_top_anchor: Anchor,
861 scroll_top_row: u32,
862}
863
864pub struct EditorCreated(pub ViewHandle<Editor>);
865
866impl Editor {
867 pub fn single_line(
868 field_editor_style: Option<GetFieldEditorTheme>,
869 cx: &mut ViewContext<Self>,
870 ) -> Self {
871 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
872 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
873 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
874 }
875
876 pub fn auto_height(
877 max_lines: usize,
878 field_editor_style: Option<GetFieldEditorTheme>,
879 cx: &mut ViewContext<Self>,
880 ) -> Self {
881 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
882 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
883 Self::new(
884 EditorMode::AutoHeight { max_lines },
885 buffer,
886 None,
887 field_editor_style,
888 cx,
889 )
890 }
891
892 pub fn for_buffer(
893 buffer: ModelHandle<Buffer>,
894 project: Option<ModelHandle<Project>>,
895 cx: &mut ViewContext<Self>,
896 ) -> Self {
897 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
898 Self::new(EditorMode::Full, buffer, project, None, cx)
899 }
900
901 pub fn for_multibuffer(
902 buffer: ModelHandle<MultiBuffer>,
903 project: Option<ModelHandle<Project>>,
904 cx: &mut ViewContext<Self>,
905 ) -> Self {
906 Self::new(EditorMode::Full, buffer, project, None, cx)
907 }
908
909 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
910 let mut clone = Self::new(
911 self.mode,
912 self.buffer.clone(),
913 self.project.clone(),
914 self.get_field_editor_theme,
915 cx,
916 );
917 clone.scroll_position = self.scroll_position;
918 clone.scroll_top_anchor = self.scroll_top_anchor.clone();
919 clone.searchable = self.searchable;
920 clone
921 }
922
923 fn new(
924 mode: EditorMode,
925 buffer: ModelHandle<MultiBuffer>,
926 project: Option<ModelHandle<Project>>,
927 get_field_editor_theme: Option<GetFieldEditorTheme>,
928 cx: &mut ViewContext<Self>,
929 ) -> Self {
930 let display_map = cx.add_model(|cx| {
931 let settings = cx.global::<Settings>();
932 let style = build_style(&*settings, get_field_editor_theme, None, cx);
933 DisplayMap::new(
934 buffer.clone(),
935 style.text.font_id,
936 style.text.font_size,
937 None,
938 2,
939 1,
940 cx,
941 )
942 });
943 cx.observe(&buffer, Self::on_buffer_changed).detach();
944 cx.subscribe(&buffer, Self::on_buffer_event).detach();
945 cx.observe(&display_map, Self::on_display_map_changed)
946 .detach();
947
948 let mut this = Self {
949 handle: cx.weak_handle(),
950 buffer,
951 display_map,
952 selections: Arc::from([]),
953 pending_selection: Some(PendingSelection {
954 selection: Selection {
955 id: 0,
956 start: Anchor::min(),
957 end: Anchor::min(),
958 reversed: false,
959 goal: SelectionGoal::None,
960 },
961 mode: SelectMode::Character,
962 }),
963 columnar_selection_tail: None,
964 next_selection_id: 1,
965 add_selections_state: None,
966 select_next_state: None,
967 selection_history: Default::default(),
968 autoclose_stack: Default::default(),
969 snippet_stack: Default::default(),
970 select_larger_syntax_node_stack: Vec::new(),
971 active_diagnostics: None,
972 soft_wrap_mode_override: None,
973 get_field_editor_theme,
974 project,
975 scroll_position: Vector2F::zero(),
976 scroll_top_anchor: Anchor::min(),
977 autoscroll_request: None,
978 focused: false,
979 show_local_cursors: false,
980 show_local_selections: true,
981 blink_epoch: 0,
982 blinking_paused: false,
983 mode,
984 vertical_scroll_margin: 3.0,
985 placeholder_text: None,
986 highlighted_rows: None,
987 background_highlights: Default::default(),
988 nav_history: None,
989 context_menu: None,
990 completion_tasks: Default::default(),
991 next_completion_id: 0,
992 available_code_actions: Default::default(),
993 code_actions_task: Default::default(),
994 document_highlights_task: Default::default(),
995 pending_rename: Default::default(),
996 searchable: true,
997 override_text_style: None,
998 cursor_shape: Default::default(),
999 keymap_context_layers: Default::default(),
1000 input_enabled: true,
1001 leader_replica_id: None,
1002 };
1003 this.end_selection(cx);
1004
1005 let editor_created_event = EditorCreated(cx.handle());
1006 cx.emit_global(editor_created_event);
1007
1008 this
1009 }
1010
1011 pub fn open_new(
1012 workspace: &mut Workspace,
1013 _: &workspace::OpenNew,
1014 cx: &mut ViewContext<Workspace>,
1015 ) {
1016 let project = workspace.project().clone();
1017 if project.read(cx).is_remote() {
1018 cx.propagate_action();
1019 } else if let Some(buffer) = project
1020 .update(cx, |project, cx| project.create_buffer("", None, cx))
1021 .log_err()
1022 {
1023 workspace.add_item(
1024 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1025 cx,
1026 );
1027 }
1028 }
1029
1030 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1031 self.buffer.read(cx).replica_id()
1032 }
1033
1034 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1035 &self.buffer
1036 }
1037
1038 pub fn title(&self, cx: &AppContext) -> String {
1039 self.buffer().read(cx).title(cx)
1040 }
1041
1042 pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
1043 EditorSnapshot {
1044 mode: self.mode,
1045 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1046 scroll_position: self.scroll_position,
1047 scroll_top_anchor: self.scroll_top_anchor.clone(),
1048 placeholder_text: self.placeholder_text.clone(),
1049 is_focused: self
1050 .handle
1051 .upgrade(cx)
1052 .map_or(false, |handle| handle.is_focused(cx)),
1053 }
1054 }
1055
1056 pub fn language_at<'a, T: ToOffset>(
1057 &self,
1058 point: T,
1059 cx: &'a AppContext,
1060 ) -> Option<&'a Arc<Language>> {
1061 self.buffer.read(cx).language_at(point, cx)
1062 }
1063
1064 fn style(&self, cx: &AppContext) -> EditorStyle {
1065 build_style(
1066 cx.global::<Settings>(),
1067 self.get_field_editor_theme,
1068 self.override_text_style.as_deref(),
1069 cx,
1070 )
1071 }
1072
1073 pub fn mode(&self) -> EditorMode {
1074 self.mode
1075 }
1076
1077 pub fn set_placeholder_text(
1078 &mut self,
1079 placeholder_text: impl Into<Arc<str>>,
1080 cx: &mut ViewContext<Self>,
1081 ) {
1082 self.placeholder_text = Some(placeholder_text.into());
1083 cx.notify();
1084 }
1085
1086 pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
1087 self.vertical_scroll_margin = margin_rows as f32;
1088 cx.notify();
1089 }
1090
1091 pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
1092 self.set_scroll_position_internal(scroll_position, true, cx);
1093 }
1094
1095 fn set_scroll_position_internal(
1096 &mut self,
1097 scroll_position: Vector2F,
1098 local: bool,
1099 cx: &mut ViewContext<Self>,
1100 ) {
1101 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1102
1103 if scroll_position.y() == 0. {
1104 self.scroll_top_anchor = Anchor::min();
1105 self.scroll_position = scroll_position;
1106 } else {
1107 let scroll_top_buffer_offset =
1108 DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
1109 let anchor = map
1110 .buffer_snapshot
1111 .anchor_at(scroll_top_buffer_offset, Bias::Right);
1112 self.scroll_position = vec2f(
1113 scroll_position.x(),
1114 scroll_position.y() - anchor.to_display_point(&map).row() as f32,
1115 );
1116 self.scroll_top_anchor = anchor;
1117 }
1118
1119 self.autoscroll_request.take();
1120 cx.emit(Event::ScrollPositionChanged { local });
1121 cx.notify();
1122 }
1123
1124 fn set_scroll_top_anchor(
1125 &mut self,
1126 anchor: Anchor,
1127 position: Vector2F,
1128 cx: &mut ViewContext<Self>,
1129 ) {
1130 self.scroll_top_anchor = anchor;
1131 self.scroll_position = position;
1132 cx.emit(Event::ScrollPositionChanged { local: false });
1133 cx.notify();
1134 }
1135
1136 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1137 self.cursor_shape = cursor_shape;
1138 cx.notify();
1139 }
1140
1141 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1142 if self.display_map.read(cx).clip_at_line_ends != clip {
1143 self.display_map
1144 .update(cx, |map, _| map.clip_at_line_ends = clip);
1145 }
1146 }
1147
1148 pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: gpui::keymap::Context) {
1149 self.keymap_context_layers
1150 .insert(TypeId::of::<Tag>(), context);
1151 }
1152
1153 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
1154 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1155 }
1156
1157 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1158 self.input_enabled = input_enabled;
1159 }
1160
1161 pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
1162 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1163 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor)
1164 }
1165
1166 pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
1167 if max < self.scroll_position.x() {
1168 self.scroll_position.set_x(max);
1169 true
1170 } else {
1171 false
1172 }
1173 }
1174
1175 pub fn autoscroll_vertically(
1176 &mut self,
1177 viewport_height: f32,
1178 line_height: f32,
1179 cx: &mut ViewContext<Self>,
1180 ) -> bool {
1181 let visible_lines = viewport_height / line_height;
1182 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1183 let mut scroll_position =
1184 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor);
1185 let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1186 (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
1187 } else {
1188 display_map.max_point().row().saturating_sub(1) as f32
1189 };
1190 if scroll_position.y() > max_scroll_top {
1191 scroll_position.set_y(max_scroll_top);
1192 self.set_scroll_position(scroll_position, cx);
1193 }
1194
1195 let (autoscroll, local) = if let Some(autoscroll) = self.autoscroll_request.take() {
1196 autoscroll
1197 } else {
1198 return false;
1199 };
1200
1201 let first_cursor_top;
1202 let last_cursor_bottom;
1203 if let Some(highlighted_rows) = &self.highlighted_rows {
1204 first_cursor_top = highlighted_rows.start as f32;
1205 last_cursor_bottom = first_cursor_top + 1.;
1206 } else if autoscroll == Autoscroll::Newest {
1207 let newest_selection =
1208 self.newest_selection_with_snapshot::<Point>(&display_map.buffer_snapshot);
1209 first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
1210 last_cursor_bottom = first_cursor_top + 1.;
1211 } else {
1212 let selections = self.local_selections::<Point>(cx);
1213 first_cursor_top = selections
1214 .first()
1215 .unwrap()
1216 .head()
1217 .to_display_point(&display_map)
1218 .row() as f32;
1219 last_cursor_bottom = selections
1220 .last()
1221 .unwrap()
1222 .head()
1223 .to_display_point(&display_map)
1224 .row() as f32
1225 + 1.0;
1226 }
1227
1228 let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1229 0.
1230 } else {
1231 ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0).floor()
1232 };
1233 if margin < 0.0 {
1234 return false;
1235 }
1236
1237 match autoscroll {
1238 Autoscroll::Fit | Autoscroll::Newest => {
1239 let margin = margin.min(self.vertical_scroll_margin);
1240 let target_top = (first_cursor_top - margin).max(0.0);
1241 let target_bottom = last_cursor_bottom + margin;
1242 let start_row = scroll_position.y();
1243 let end_row = start_row + visible_lines;
1244
1245 if target_top < start_row {
1246 scroll_position.set_y(target_top);
1247 self.set_scroll_position_internal(scroll_position, local, cx);
1248 } else if target_bottom >= end_row {
1249 scroll_position.set_y(target_bottom - visible_lines);
1250 self.set_scroll_position_internal(scroll_position, local, cx);
1251 }
1252 }
1253 Autoscroll::Center => {
1254 scroll_position.set_y((first_cursor_top - margin).max(0.0));
1255 self.set_scroll_position_internal(scroll_position, local, cx);
1256 }
1257 }
1258
1259 true
1260 }
1261
1262 pub fn autoscroll_horizontally(
1263 &mut self,
1264 start_row: u32,
1265 viewport_width: f32,
1266 scroll_width: f32,
1267 max_glyph_width: f32,
1268 layouts: &[text_layout::Line],
1269 cx: &mut ViewContext<Self>,
1270 ) -> bool {
1271 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1272 let selections = self.local_selections::<Point>(cx);
1273
1274 let mut target_left;
1275 let mut target_right;
1276
1277 if self.highlighted_rows.is_some() {
1278 target_left = 0.0_f32;
1279 target_right = 0.0_f32;
1280 } else {
1281 target_left = std::f32::INFINITY;
1282 target_right = 0.0_f32;
1283 for selection in selections {
1284 let head = selection.head().to_display_point(&display_map);
1285 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
1286 let start_column = head.column().saturating_sub(3);
1287 let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
1288 target_left = target_left.min(
1289 layouts[(head.row() - start_row) as usize]
1290 .x_for_index(start_column as usize),
1291 );
1292 target_right = target_right.max(
1293 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
1294 + max_glyph_width,
1295 );
1296 }
1297 }
1298 }
1299
1300 target_right = target_right.min(scroll_width);
1301
1302 if target_right - target_left > viewport_width {
1303 return false;
1304 }
1305
1306 let scroll_left = self.scroll_position.x() * max_glyph_width;
1307 let scroll_right = scroll_left + viewport_width;
1308
1309 if target_left < scroll_left {
1310 self.scroll_position.set_x(target_left / max_glyph_width);
1311 true
1312 } else if target_right > scroll_right {
1313 self.scroll_position
1314 .set_x((target_right - viewport_width) / max_glyph_width);
1315 true
1316 } else {
1317 false
1318 }
1319 }
1320
1321 pub fn replace_selections_with(
1322 &mut self,
1323 cx: &mut ViewContext<Self>,
1324 mut find_replacement: impl FnMut(&DisplaySnapshot) -> DisplayPoint,
1325 ) {
1326 let display_map = self.snapshot(cx);
1327 let cursor = find_replacement(&display_map);
1328 let selection = Selection {
1329 id: post_inc(&mut self.next_selection_id),
1330 start: cursor,
1331 end: cursor,
1332 reversed: false,
1333 goal: SelectionGoal::None,
1334 }
1335 .map(|display_point| display_point.to_point(&display_map));
1336 self.update_selections(vec![selection], None, cx);
1337 }
1338
1339 pub fn display_selections(
1340 &mut self,
1341 cx: &mut ViewContext<Self>,
1342 ) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
1343 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1344 let selections = self
1345 .local_selections::<Point>(cx)
1346 .into_iter()
1347 .map(|selection| selection.map(|point| point.to_display_point(&display_map)))
1348 .collect();
1349 (display_map, selections)
1350 }
1351
1352 pub fn move_selections(
1353 &mut self,
1354 cx: &mut ViewContext<Self>,
1355 mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection<DisplayPoint>),
1356 ) {
1357 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1358 let selections = self
1359 .local_selections::<Point>(cx)
1360 .into_iter()
1361 .map(|selection| {
1362 let mut selection = selection.map(|point| point.to_display_point(&display_map));
1363 move_selection(&display_map, &mut selection);
1364 selection.map(|display_point| display_point.to_point(&display_map))
1365 })
1366 .collect();
1367 self.update_selections(selections, Some(Autoscroll::Fit), cx);
1368 }
1369
1370 pub fn move_selection_heads(
1371 &mut self,
1372 cx: &mut ViewContext<Self>,
1373 mut update_head: impl FnMut(
1374 &DisplaySnapshot,
1375 DisplayPoint,
1376 SelectionGoal,
1377 ) -> (DisplayPoint, SelectionGoal),
1378 ) {
1379 self.move_selections(cx, |map, selection| {
1380 let (new_head, new_goal) = update_head(map, selection.head(), selection.goal);
1381 selection.set_head(new_head, new_goal);
1382 });
1383 }
1384
1385 pub fn move_cursors(
1386 &mut self,
1387 cx: &mut ViewContext<Self>,
1388 mut update_cursor_position: impl FnMut(
1389 &DisplaySnapshot,
1390 DisplayPoint,
1391 SelectionGoal,
1392 ) -> (DisplayPoint, SelectionGoal),
1393 ) {
1394 self.move_selections(cx, |map, selection| {
1395 let (cursor, new_goal) = update_cursor_position(map, selection.head(), selection.goal);
1396 selection.collapse_to(cursor, new_goal)
1397 });
1398 }
1399
1400 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1401 where
1402 I: IntoIterator<Item = (Range<S>, T)>,
1403 S: ToOffset,
1404 T: Into<Arc<str>>,
1405 {
1406 self.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
1407 }
1408
1409 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1410 where
1411 I: IntoIterator<Item = (Range<S>, T)>,
1412 S: ToOffset,
1413 T: Into<Arc<str>>,
1414 {
1415 self.buffer
1416 .update(cx, |buffer, cx| buffer.edit_with_autoindent(edits, cx));
1417 }
1418
1419 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
1420 self.hide_context_menu(cx);
1421
1422 match phase {
1423 SelectPhase::Begin {
1424 position,
1425 add,
1426 click_count,
1427 } => self.begin_selection(*position, *add, *click_count, cx),
1428 SelectPhase::BeginColumnar {
1429 position,
1430 overshoot,
1431 } => self.begin_columnar_selection(*position, *overshoot, cx),
1432 SelectPhase::Extend {
1433 position,
1434 click_count,
1435 } => self.extend_selection(*position, *click_count, cx),
1436 SelectPhase::Update {
1437 position,
1438 overshoot,
1439 scroll_position,
1440 } => self.update_selection(*position, *overshoot, *scroll_position, cx),
1441 SelectPhase::End => self.end_selection(cx),
1442 }
1443 }
1444
1445 fn extend_selection(
1446 &mut self,
1447 position: DisplayPoint,
1448 click_count: usize,
1449 cx: &mut ViewContext<Self>,
1450 ) {
1451 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1452 let tail = self
1453 .newest_selection_with_snapshot::<usize>(&display_map.buffer_snapshot)
1454 .tail();
1455 self.begin_selection(position, false, click_count, cx);
1456
1457 let position = position.to_offset(&display_map, Bias::Left);
1458 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1459 let mut pending = self.pending_selection.clone().unwrap();
1460
1461 if position >= tail {
1462 pending.selection.start = tail_anchor.clone();
1463 } else {
1464 pending.selection.end = tail_anchor.clone();
1465 pending.selection.reversed = true;
1466 }
1467
1468 match &mut pending.mode {
1469 SelectMode::Word(range) | SelectMode::Line(range) => {
1470 *range = tail_anchor.clone()..tail_anchor
1471 }
1472 _ => {}
1473 }
1474
1475 self.set_selections(self.selections.clone(), Some(pending), true, cx);
1476 }
1477
1478 fn begin_selection(
1479 &mut self,
1480 position: DisplayPoint,
1481 add: bool,
1482 click_count: usize,
1483 cx: &mut ViewContext<Self>,
1484 ) {
1485 if !self.focused {
1486 cx.focus_self();
1487 cx.emit(Event::Activate);
1488 }
1489
1490 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1491 let buffer = &display_map.buffer_snapshot;
1492 let newest_selection = self.newest_anchor_selection().clone();
1493 let position = display_map.clip_point(position, Bias::Left);
1494
1495 let start;
1496 let end;
1497 let mode;
1498 match click_count {
1499 1 => {
1500 start = buffer.anchor_before(position.to_point(&display_map));
1501 end = start.clone();
1502 mode = SelectMode::Character;
1503 }
1504 2 => {
1505 let range = movement::surrounding_word(&display_map, position);
1506 start = buffer.anchor_before(range.start.to_point(&display_map));
1507 end = buffer.anchor_before(range.end.to_point(&display_map));
1508 mode = SelectMode::Word(start.clone()..end.clone());
1509 }
1510 3 => {
1511 let position = display_map
1512 .clip_point(position, Bias::Left)
1513 .to_point(&display_map);
1514 let line_start = display_map.prev_line_boundary(position).0;
1515 let next_line_start = buffer.clip_point(
1516 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1517 Bias::Left,
1518 );
1519 start = buffer.anchor_before(line_start);
1520 end = buffer.anchor_before(next_line_start);
1521 mode = SelectMode::Line(start.clone()..end.clone());
1522 }
1523 _ => {
1524 start = buffer.anchor_before(0);
1525 end = buffer.anchor_before(buffer.len());
1526 mode = SelectMode::All;
1527 }
1528 }
1529
1530 let selection = Selection {
1531 id: post_inc(&mut self.next_selection_id),
1532 start,
1533 end,
1534 reversed: false,
1535 goal: SelectionGoal::None,
1536 };
1537
1538 let mut selections;
1539 if add {
1540 selections = self.selections.clone();
1541 // Remove the newest selection if it was added due to a previous mouse up
1542 // within this multi-click.
1543 if click_count > 1 {
1544 selections = self
1545 .selections
1546 .iter()
1547 .filter(|selection| selection.id != newest_selection.id)
1548 .cloned()
1549 .collect();
1550 }
1551 } else {
1552 selections = Arc::from([]);
1553 }
1554 self.set_selections(
1555 selections,
1556 Some(PendingSelection { selection, mode }),
1557 true,
1558 cx,
1559 );
1560
1561 cx.notify();
1562 }
1563
1564 fn begin_columnar_selection(
1565 &mut self,
1566 position: DisplayPoint,
1567 overshoot: u32,
1568 cx: &mut ViewContext<Self>,
1569 ) {
1570 if !self.focused {
1571 cx.focus_self();
1572 cx.emit(Event::Activate);
1573 }
1574
1575 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1576 let tail = self
1577 .newest_selection_with_snapshot::<Point>(&display_map.buffer_snapshot)
1578 .tail();
1579 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1580
1581 self.select_columns(
1582 tail.to_display_point(&display_map),
1583 position,
1584 overshoot,
1585 &display_map,
1586 cx,
1587 );
1588 }
1589
1590 fn update_selection(
1591 &mut self,
1592 position: DisplayPoint,
1593 overshoot: u32,
1594 scroll_position: Vector2F,
1595 cx: &mut ViewContext<Self>,
1596 ) {
1597 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1598
1599 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1600 let tail = tail.to_display_point(&display_map);
1601 self.select_columns(tail, position, overshoot, &display_map, cx);
1602 } else if let Some(mut pending) = self.pending_selection.clone() {
1603 let buffer = self.buffer.read(cx).snapshot(cx);
1604 let head;
1605 let tail;
1606 match &pending.mode {
1607 SelectMode::Character => {
1608 head = position.to_point(&display_map);
1609 tail = pending.selection.tail().to_point(&buffer);
1610 }
1611 SelectMode::Word(original_range) => {
1612 let original_display_range = original_range.start.to_display_point(&display_map)
1613 ..original_range.end.to_display_point(&display_map);
1614 let original_buffer_range = original_display_range.start.to_point(&display_map)
1615 ..original_display_range.end.to_point(&display_map);
1616 if movement::is_inside_word(&display_map, position)
1617 || original_display_range.contains(&position)
1618 {
1619 let word_range = movement::surrounding_word(&display_map, position);
1620 if word_range.start < original_display_range.start {
1621 head = word_range.start.to_point(&display_map);
1622 } else {
1623 head = word_range.end.to_point(&display_map);
1624 }
1625 } else {
1626 head = position.to_point(&display_map);
1627 }
1628
1629 if head <= original_buffer_range.start {
1630 tail = original_buffer_range.end;
1631 } else {
1632 tail = original_buffer_range.start;
1633 }
1634 }
1635 SelectMode::Line(original_range) => {
1636 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1637
1638 let position = display_map
1639 .clip_point(position, Bias::Left)
1640 .to_point(&display_map);
1641 let line_start = display_map.prev_line_boundary(position).0;
1642 let next_line_start = buffer.clip_point(
1643 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1644 Bias::Left,
1645 );
1646
1647 if line_start < original_range.start {
1648 head = line_start
1649 } else {
1650 head = next_line_start
1651 }
1652
1653 if head <= original_range.start {
1654 tail = original_range.end;
1655 } else {
1656 tail = original_range.start;
1657 }
1658 }
1659 SelectMode::All => {
1660 return;
1661 }
1662 };
1663
1664 if head < tail {
1665 pending.selection.start = buffer.anchor_before(head);
1666 pending.selection.end = buffer.anchor_before(tail);
1667 pending.selection.reversed = true;
1668 } else {
1669 pending.selection.start = buffer.anchor_before(tail);
1670 pending.selection.end = buffer.anchor_before(head);
1671 pending.selection.reversed = false;
1672 }
1673 self.set_selections(self.selections.clone(), Some(pending), true, cx);
1674 } else {
1675 log::error!("update_selection dispatched with no pending selection");
1676 return;
1677 }
1678
1679 self.set_scroll_position(scroll_position, cx);
1680 cx.notify();
1681 }
1682
1683 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1684 self.columnar_selection_tail.take();
1685 if self.pending_selection.is_some() {
1686 let selections = self.local_selections::<usize>(cx);
1687 self.update_selections(selections, None, cx);
1688 }
1689 }
1690
1691 fn select_columns(
1692 &mut self,
1693 tail: DisplayPoint,
1694 head: DisplayPoint,
1695 overshoot: u32,
1696 display_map: &DisplaySnapshot,
1697 cx: &mut ViewContext<Self>,
1698 ) {
1699 let start_row = cmp::min(tail.row(), head.row());
1700 let end_row = cmp::max(tail.row(), head.row());
1701 let start_column = cmp::min(tail.column(), head.column() + overshoot);
1702 let end_column = cmp::max(tail.column(), head.column() + overshoot);
1703 let reversed = start_column < tail.column();
1704
1705 let selections = (start_row..=end_row)
1706 .filter_map(|row| {
1707 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1708 let start = display_map
1709 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1710 .to_point(&display_map);
1711 let end = display_map
1712 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1713 .to_point(&display_map);
1714 Some(Selection {
1715 id: post_inc(&mut self.next_selection_id),
1716 start,
1717 end,
1718 reversed,
1719 goal: SelectionGoal::None,
1720 })
1721 } else {
1722 None
1723 }
1724 })
1725 .collect::<Vec<_>>();
1726
1727 self.update_selections(selections, None, cx);
1728 cx.notify();
1729 }
1730
1731 pub fn is_selecting(&self) -> bool {
1732 self.pending_selection.is_some() || self.columnar_selection_tail.is_some()
1733 }
1734
1735 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1736 if self.take_rename(false, cx).is_some() {
1737 return;
1738 }
1739
1740 if self.hide_context_menu(cx).is_some() {
1741 return;
1742 }
1743
1744 if self.snippet_stack.pop().is_some() {
1745 return;
1746 }
1747
1748 if self.mode == EditorMode::Full {
1749 if self.active_diagnostics.is_some() {
1750 self.dismiss_diagnostics(cx);
1751 return;
1752 }
1753
1754 if let Some(pending) = self.pending_selection.clone() {
1755 let mut selections = self.selections.clone();
1756 if selections.is_empty() {
1757 selections = Arc::from([pending.selection]);
1758 }
1759 self.set_selections(selections, None, true, cx);
1760 self.request_autoscroll(Autoscroll::Fit, cx);
1761 return;
1762 }
1763
1764 let mut oldest_selection = self.oldest_selection::<usize>(&cx);
1765 if self.selection_count() > 1 {
1766 self.update_selections(vec![oldest_selection], Some(Autoscroll::Fit), cx);
1767 return;
1768 }
1769
1770 if !oldest_selection.is_empty() {
1771 oldest_selection.start = oldest_selection.head().clone();
1772 oldest_selection.end = oldest_selection.head().clone();
1773 self.update_selections(vec![oldest_selection], Some(Autoscroll::Fit), cx);
1774 return;
1775 }
1776 }
1777
1778 cx.propagate_action();
1779 }
1780
1781 #[cfg(any(test, feature = "test-support"))]
1782 pub fn selected_ranges<D: TextDimension + Ord + Sub<D, Output = D>>(
1783 &self,
1784 cx: &AppContext,
1785 ) -> Vec<Range<D>> {
1786 self.local_selections::<D>(cx)
1787 .iter()
1788 .map(|s| {
1789 if s.reversed {
1790 s.end.clone()..s.start.clone()
1791 } else {
1792 s.start.clone()..s.end.clone()
1793 }
1794 })
1795 .collect()
1796 }
1797
1798 #[cfg(any(test, feature = "test-support"))]
1799 pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec<Range<DisplayPoint>> {
1800 let display_map = self
1801 .display_map
1802 .update(cx, |display_map, cx| display_map.snapshot(cx));
1803 self.selections
1804 .iter()
1805 .chain(
1806 self.pending_selection
1807 .as_ref()
1808 .map(|pending| &pending.selection),
1809 )
1810 .map(|s| {
1811 if s.reversed {
1812 s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
1813 } else {
1814 s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
1815 }
1816 })
1817 .collect()
1818 }
1819
1820 pub fn select_ranges<I, T>(
1821 &mut self,
1822 ranges: I,
1823 autoscroll: Option<Autoscroll>,
1824 cx: &mut ViewContext<Self>,
1825 ) where
1826 I: IntoIterator<Item = Range<T>>,
1827 T: ToOffset,
1828 {
1829 let buffer = self.buffer.read(cx).snapshot(cx);
1830 let selections = ranges
1831 .into_iter()
1832 .map(|range| {
1833 let mut start = range.start.to_offset(&buffer);
1834 let mut end = range.end.to_offset(&buffer);
1835 let reversed = if start > end {
1836 mem::swap(&mut start, &mut end);
1837 true
1838 } else {
1839 false
1840 };
1841 Selection {
1842 id: post_inc(&mut self.next_selection_id),
1843 start,
1844 end,
1845 reversed,
1846 goal: SelectionGoal::None,
1847 }
1848 })
1849 .collect::<Vec<_>>();
1850 self.update_selections(selections, autoscroll, cx);
1851 }
1852
1853 #[cfg(any(test, feature = "test-support"))]
1854 pub fn select_display_ranges<'a, T>(&mut self, ranges: T, cx: &mut ViewContext<Self>)
1855 where
1856 T: IntoIterator<Item = &'a Range<DisplayPoint>>,
1857 {
1858 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1859 let selections = ranges
1860 .into_iter()
1861 .map(|range| {
1862 let mut start = range.start;
1863 let mut end = range.end;
1864 let reversed = if start > end {
1865 mem::swap(&mut start, &mut end);
1866 true
1867 } else {
1868 false
1869 };
1870 Selection {
1871 id: post_inc(&mut self.next_selection_id),
1872 start: start.to_point(&display_map),
1873 end: end.to_point(&display_map),
1874 reversed,
1875 goal: SelectionGoal::None,
1876 }
1877 })
1878 .collect();
1879 self.update_selections(selections, None, cx);
1880 }
1881
1882 pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext<Self>) {
1883 if !self.input_enabled {
1884 cx.propagate_action();
1885 return;
1886 }
1887
1888 let text = action.0.as_ref();
1889 if !self.skip_autoclose_end(text, cx) {
1890 self.transact(cx, |this, cx| {
1891 if !this.surround_with_bracket_pair(text, cx) {
1892 this.insert(text, cx);
1893 this.autoclose_bracket_pairs(cx);
1894 }
1895 });
1896 self.trigger_completion_on_input(text, cx);
1897 }
1898 }
1899
1900 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
1901 self.transact(cx, |this, cx| {
1902 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
1903 let selections = this.local_selections::<usize>(cx);
1904 let buffer = this.buffer.read(cx).snapshot(cx);
1905 selections
1906 .iter()
1907 .map(|selection| {
1908 let start_point = selection.start.to_point(&buffer);
1909 let indent = buffer
1910 .indent_column_for_line(start_point.row)
1911 .min(start_point.column);
1912 let start = selection.start;
1913 let end = selection.end;
1914
1915 let mut insert_extra_newline = false;
1916 if let Some(language) = buffer.language() {
1917 let leading_whitespace_len = buffer
1918 .reversed_chars_at(start)
1919 .take_while(|c| c.is_whitespace() && *c != '\n')
1920 .map(|c| c.len_utf8())
1921 .sum::<usize>();
1922
1923 let trailing_whitespace_len = buffer
1924 .chars_at(end)
1925 .take_while(|c| c.is_whitespace() && *c != '\n')
1926 .map(|c| c.len_utf8())
1927 .sum::<usize>();
1928
1929 insert_extra_newline = language.brackets().iter().any(|pair| {
1930 let pair_start = pair.start.trim_end();
1931 let pair_end = pair.end.trim_start();
1932
1933 pair.newline
1934 && buffer
1935 .contains_str_at(end + trailing_whitespace_len, pair_end)
1936 && buffer.contains_str_at(
1937 (start - leading_whitespace_len)
1938 .saturating_sub(pair_start.len()),
1939 pair_start,
1940 )
1941 });
1942 }
1943
1944 let mut new_text = String::with_capacity(1 + indent as usize);
1945 new_text.push('\n');
1946 new_text.extend(iter::repeat(' ').take(indent as usize));
1947 if insert_extra_newline {
1948 new_text = new_text.repeat(2);
1949 }
1950 (
1951 (start..end, new_text),
1952 (insert_extra_newline, buffer.anchor_after(end)),
1953 )
1954 })
1955 .unzip()
1956 };
1957
1958 this.buffer.update(cx, |buffer, cx| {
1959 buffer.edit_with_autoindent(edits, cx);
1960
1961 let buffer = buffer.read(cx);
1962 this.selections = this
1963 .selections
1964 .iter()
1965 .cloned()
1966 .zip(selection_fixup_info)
1967 .map(|(mut new_selection, (extra_newline_inserted, end))| {
1968 let mut cursor = end.to_point(&buffer);
1969 if extra_newline_inserted {
1970 cursor.row -= 1;
1971 cursor.column = buffer.line_len(cursor.row);
1972 }
1973 let anchor = buffer.anchor_after(cursor);
1974 new_selection.start = anchor.clone();
1975 new_selection.end = anchor;
1976 new_selection
1977 })
1978 .collect();
1979 });
1980
1981 this.request_autoscroll(Autoscroll::Fit, cx);
1982 });
1983 }
1984
1985 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1986 let text: Arc<str> = text.into();
1987 self.transact(cx, |this, cx| {
1988 let old_selections = this.local_selections::<usize>(cx);
1989 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
1990 let anchors = {
1991 let snapshot = buffer.read(cx);
1992 old_selections
1993 .iter()
1994 .map(|s| (s.id, s.goal, snapshot.anchor_after(s.end)))
1995 .collect::<Vec<_>>()
1996 };
1997 buffer.edit_with_autoindent(
1998 old_selections
1999 .iter()
2000 .map(|s| (s.start..s.end, text.clone())),
2001 cx,
2002 );
2003 anchors
2004 });
2005
2006 let selections = {
2007 let snapshot = this.buffer.read(cx).read(cx);
2008 selection_anchors
2009 .into_iter()
2010 .map(|(id, goal, position)| {
2011 let position = position.to_offset(&snapshot);
2012 Selection {
2013 id,
2014 start: position,
2015 end: position,
2016 goal,
2017 reversed: false,
2018 }
2019 })
2020 .collect()
2021 };
2022 this.update_selections(selections, Some(Autoscroll::Fit), cx);
2023 });
2024 }
2025
2026 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2027 let selection = self.newest_anchor_selection();
2028 if self
2029 .buffer
2030 .read(cx)
2031 .is_completion_trigger(selection.head(), text, cx)
2032 {
2033 self.show_completions(&ShowCompletions, cx);
2034 } else {
2035 self.hide_context_menu(cx);
2036 }
2037 }
2038
2039 fn surround_with_bracket_pair(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
2040 let snapshot = self.buffer.read(cx).snapshot(cx);
2041 if let Some(pair) = snapshot
2042 .language()
2043 .and_then(|language| language.brackets().iter().find(|b| b.start == text))
2044 .cloned()
2045 {
2046 if self
2047 .local_selections::<usize>(cx)
2048 .iter()
2049 .any(|selection| selection.is_empty())
2050 {
2051 false
2052 } else {
2053 let mut selections = self.selections.to_vec();
2054 for selection in &mut selections {
2055 selection.end = selection.end.bias_left(&snapshot);
2056 }
2057 drop(snapshot);
2058
2059 self.buffer.update(cx, |buffer, cx| {
2060 let pair_start: Arc<str> = pair.start.clone().into();
2061 buffer.edit(
2062 selections
2063 .iter()
2064 .map(|s| (s.start.clone()..s.start.clone(), pair_start.clone())),
2065 cx,
2066 );
2067 let pair_end: Arc<str> = pair.end.clone().into();
2068 buffer.edit(
2069 selections
2070 .iter()
2071 .map(|s| (s.end.clone()..s.end.clone(), pair_end.clone())),
2072 cx,
2073 );
2074 });
2075
2076 let snapshot = self.buffer.read(cx).read(cx);
2077 for selection in &mut selections {
2078 selection.end = selection.end.bias_right(&snapshot);
2079 }
2080 drop(snapshot);
2081
2082 self.set_selections(selections.into(), None, true, cx);
2083 true
2084 }
2085 } else {
2086 false
2087 }
2088 }
2089
2090 fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext<Self>) {
2091 let selections = self.local_selections::<usize>(cx);
2092 let mut bracket_pair_state = None;
2093 let mut new_selections = None;
2094 self.buffer.update(cx, |buffer, cx| {
2095 let mut snapshot = buffer.snapshot(cx);
2096 let left_biased_selections = selections
2097 .iter()
2098 .map(|selection| Selection {
2099 id: selection.id,
2100 start: snapshot.anchor_before(selection.start),
2101 end: snapshot.anchor_before(selection.end),
2102 reversed: selection.reversed,
2103 goal: selection.goal,
2104 })
2105 .collect::<Vec<_>>();
2106
2107 let autoclose_pair = snapshot.language().and_then(|language| {
2108 let first_selection_start = selections.first().unwrap().start;
2109 let pair = language.brackets().iter().find(|pair| {
2110 snapshot.contains_str_at(
2111 first_selection_start.saturating_sub(pair.start.len()),
2112 &pair.start,
2113 )
2114 });
2115 pair.and_then(|pair| {
2116 let should_autoclose = selections.iter().all(|selection| {
2117 // Ensure all selections are parked at the end of a pair start.
2118 if snapshot.contains_str_at(
2119 selection.start.saturating_sub(pair.start.len()),
2120 &pair.start,
2121 ) {
2122 snapshot
2123 .chars_at(selection.start)
2124 .next()
2125 .map_or(true, |c| language.should_autoclose_before(c))
2126 } else {
2127 false
2128 }
2129 });
2130
2131 if should_autoclose {
2132 Some(pair.clone())
2133 } else {
2134 None
2135 }
2136 })
2137 });
2138
2139 if let Some(pair) = autoclose_pair {
2140 let selection_ranges = selections
2141 .iter()
2142 .map(|selection| {
2143 let start = selection.start.to_offset(&snapshot);
2144 start..start
2145 })
2146 .collect::<SmallVec<[_; 32]>>();
2147
2148 let pair_end: Arc<str> = pair.end.clone().into();
2149 buffer.edit(
2150 selection_ranges
2151 .iter()
2152 .map(|range| (range.clone(), pair_end.clone())),
2153 cx,
2154 );
2155 snapshot = buffer.snapshot(cx);
2156
2157 new_selections = Some(
2158 self.resolve_selections::<usize, _>(left_biased_selections.iter(), &snapshot)
2159 .collect::<Vec<_>>(),
2160 );
2161
2162 if pair.end.len() == 1 {
2163 let mut delta = 0;
2164 bracket_pair_state = Some(BracketPairState {
2165 ranges: selections
2166 .iter()
2167 .map(move |selection| {
2168 let offset = selection.start + delta;
2169 delta += 1;
2170 snapshot.anchor_before(offset)..snapshot.anchor_after(offset)
2171 })
2172 .collect(),
2173 pair,
2174 });
2175 }
2176 }
2177 });
2178
2179 if let Some(new_selections) = new_selections {
2180 self.update_selections(new_selections, None, cx);
2181 }
2182 if let Some(bracket_pair_state) = bracket_pair_state {
2183 self.autoclose_stack.push(bracket_pair_state);
2184 }
2185 }
2186
2187 fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
2188 let old_selections = self.local_selections::<usize>(cx);
2189 let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
2190 autoclose_pair
2191 } else {
2192 return false;
2193 };
2194 if text != autoclose_pair.pair.end {
2195 return false;
2196 }
2197
2198 debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
2199
2200 let buffer = self.buffer.read(cx).snapshot(cx);
2201 if old_selections
2202 .iter()
2203 .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
2204 .all(|(selection, autoclose_range)| {
2205 let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
2206 selection.is_empty() && selection.start == autoclose_range_end
2207 })
2208 {
2209 let new_selections = old_selections
2210 .into_iter()
2211 .map(|selection| {
2212 let cursor = selection.start + 1;
2213 Selection {
2214 id: selection.id,
2215 start: cursor,
2216 end: cursor,
2217 reversed: false,
2218 goal: SelectionGoal::None,
2219 }
2220 })
2221 .collect();
2222 self.autoclose_stack.pop();
2223 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
2224 true
2225 } else {
2226 false
2227 }
2228 }
2229
2230 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2231 let offset = position.to_offset(buffer);
2232 let (word_range, kind) = buffer.surrounding_word(offset);
2233 if offset > word_range.start && kind == Some(CharKind::Word) {
2234 Some(
2235 buffer
2236 .text_for_range(word_range.start..offset)
2237 .collect::<String>(),
2238 )
2239 } else {
2240 None
2241 }
2242 }
2243
2244 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2245 if self.pending_rename.is_some() {
2246 return;
2247 }
2248
2249 let project = if let Some(project) = self.project.clone() {
2250 project
2251 } else {
2252 return;
2253 };
2254
2255 let position = self.newest_anchor_selection().head();
2256 let (buffer, buffer_position) = if let Some(output) = self
2257 .buffer
2258 .read(cx)
2259 .text_anchor_for_position(position.clone(), cx)
2260 {
2261 output
2262 } else {
2263 return;
2264 };
2265
2266 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2267 let completions = project.update(cx, |project, cx| {
2268 project.completions(&buffer, buffer_position.clone(), cx)
2269 });
2270
2271 let id = post_inc(&mut self.next_completion_id);
2272 let task = cx.spawn_weak(|this, mut cx| {
2273 async move {
2274 let completions = completions.await?;
2275 if completions.is_empty() {
2276 return Ok(());
2277 }
2278
2279 let mut menu = CompletionsMenu {
2280 id,
2281 initial_position: position,
2282 match_candidates: completions
2283 .iter()
2284 .enumerate()
2285 .map(|(id, completion)| {
2286 StringMatchCandidate::new(
2287 id,
2288 completion.label.text[completion.label.filter_range.clone()].into(),
2289 )
2290 })
2291 .collect(),
2292 buffer,
2293 completions: completions.into(),
2294 matches: Vec::new().into(),
2295 selected_item: 0,
2296 list: Default::default(),
2297 };
2298
2299 menu.filter(query.as_deref(), cx.background()).await;
2300
2301 if let Some(this) = this.upgrade(&cx) {
2302 this.update(&mut cx, |this, cx| {
2303 match this.context_menu.as_ref() {
2304 None => {}
2305 Some(ContextMenu::Completions(prev_menu)) => {
2306 if prev_menu.id > menu.id {
2307 return;
2308 }
2309 }
2310 _ => return,
2311 }
2312
2313 this.completion_tasks.retain(|(id, _)| *id > menu.id);
2314 if this.focused {
2315 this.show_context_menu(ContextMenu::Completions(menu), cx);
2316 }
2317
2318 cx.notify();
2319 });
2320 }
2321 Ok::<_, anyhow::Error>(())
2322 }
2323 .log_err()
2324 });
2325 self.completion_tasks.push((id, task));
2326 }
2327
2328 pub fn confirm_completion(
2329 &mut self,
2330 action: &ConfirmCompletion,
2331 cx: &mut ViewContext<Self>,
2332 ) -> Option<Task<Result<()>>> {
2333 use language::ToOffset as _;
2334
2335 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2336 menu
2337 } else {
2338 return None;
2339 };
2340
2341 let mat = completions_menu
2342 .matches
2343 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2344 let buffer_handle = completions_menu.buffer;
2345 let completion = completions_menu.completions.get(mat.candidate_id)?;
2346
2347 let snippet;
2348 let text;
2349 if completion.is_snippet() {
2350 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2351 text = snippet.as_ref().unwrap().text.clone();
2352 } else {
2353 snippet = None;
2354 text = completion.new_text.clone();
2355 };
2356 let buffer = buffer_handle.read(cx);
2357 let old_range = completion.old_range.to_offset(&buffer);
2358 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2359
2360 let selections = self.local_selections::<usize>(cx);
2361 let newest_selection = self.newest_anchor_selection();
2362 if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
2363 return None;
2364 }
2365
2366 let lookbehind = newest_selection
2367 .start
2368 .text_anchor
2369 .to_offset(buffer)
2370 .saturating_sub(old_range.start);
2371 let lookahead = old_range
2372 .end
2373 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2374 let mut common_prefix_len = old_text
2375 .bytes()
2376 .zip(text.bytes())
2377 .take_while(|(a, b)| a == b)
2378 .count();
2379
2380 let snapshot = self.buffer.read(cx).snapshot(cx);
2381 let mut ranges = Vec::new();
2382 for selection in &selections {
2383 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2384 let start = selection.start.saturating_sub(lookbehind);
2385 let end = selection.end + lookahead;
2386 ranges.push(start + common_prefix_len..end);
2387 } else {
2388 common_prefix_len = 0;
2389 ranges.clear();
2390 ranges.extend(selections.iter().map(|s| {
2391 if s.id == newest_selection.id {
2392 old_range.clone()
2393 } else {
2394 s.start..s.end
2395 }
2396 }));
2397 break;
2398 }
2399 }
2400 let text = &text[common_prefix_len..];
2401
2402 self.transact(cx, |this, cx| {
2403 if let Some(mut snippet) = snippet {
2404 snippet.text = text.to_string();
2405 for tabstop in snippet.tabstops.iter_mut().flatten() {
2406 tabstop.start -= common_prefix_len as isize;
2407 tabstop.end -= common_prefix_len as isize;
2408 }
2409
2410 this.insert_snippet(&ranges, snippet, cx).log_err();
2411 } else {
2412 this.buffer.update(cx, |buffer, cx| {
2413 buffer
2414 .edit_with_autoindent(ranges.iter().map(|range| (range.clone(), text)), cx);
2415 });
2416 }
2417 });
2418
2419 let project = self.project.clone()?;
2420 let apply_edits = project.update(cx, |project, cx| {
2421 project.apply_additional_edits_for_completion(
2422 buffer_handle,
2423 completion.clone(),
2424 true,
2425 cx,
2426 )
2427 });
2428 Some(cx.foreground().spawn(async move {
2429 apply_edits.await?;
2430 Ok(())
2431 }))
2432 }
2433
2434 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2435 if matches!(
2436 self.context_menu.as_ref(),
2437 Some(ContextMenu::CodeActions(_))
2438 ) {
2439 self.context_menu.take();
2440 cx.notify();
2441 return;
2442 }
2443
2444 let deployed_from_indicator = action.deployed_from_indicator;
2445 let mut task = self.code_actions_task.take();
2446 cx.spawn_weak(|this, mut cx| async move {
2447 while let Some(prev_task) = task {
2448 prev_task.await;
2449 task = this
2450 .upgrade(&cx)
2451 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2452 }
2453
2454 if let Some(this) = this.upgrade(&cx) {
2455 this.update(&mut cx, |this, cx| {
2456 if this.focused {
2457 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2458 this.show_context_menu(
2459 ContextMenu::CodeActions(CodeActionsMenu {
2460 buffer,
2461 actions,
2462 selected_item: Default::default(),
2463 list: Default::default(),
2464 deployed_from_indicator,
2465 }),
2466 cx,
2467 );
2468 }
2469 }
2470 })
2471 }
2472 Ok::<_, anyhow::Error>(())
2473 })
2474 .detach_and_log_err(cx);
2475 }
2476
2477 pub fn confirm_code_action(
2478 workspace: &mut Workspace,
2479 action: &ConfirmCodeAction,
2480 cx: &mut ViewContext<Workspace>,
2481 ) -> Option<Task<Result<()>>> {
2482 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2483 let actions_menu = if let ContextMenu::CodeActions(menu) =
2484 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2485 {
2486 menu
2487 } else {
2488 return None;
2489 };
2490 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2491 let action = actions_menu.actions.get(action_ix)?.clone();
2492 let title = action.lsp_action.title.clone();
2493 let buffer = actions_menu.buffer;
2494
2495 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2496 project.apply_code_action(buffer, action, true, cx)
2497 });
2498 Some(cx.spawn(|workspace, cx| async move {
2499 let project_transaction = apply_code_actions.await?;
2500 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2501 }))
2502 }
2503
2504 async fn open_project_transaction(
2505 this: ViewHandle<Editor>,
2506 workspace: ViewHandle<Workspace>,
2507 transaction: ProjectTransaction,
2508 title: String,
2509 mut cx: AsyncAppContext,
2510 ) -> Result<()> {
2511 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2512
2513 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2514 entries.sort_unstable_by_key(|(buffer, _)| {
2515 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2516 });
2517
2518 // If the project transaction's edits are all contained within this editor, then
2519 // avoid opening a new editor to display them.
2520
2521 if let Some((buffer, transaction)) = entries.first() {
2522 if entries.len() == 1 {
2523 let excerpt = this.read_with(&cx, |editor, cx| {
2524 editor
2525 .buffer()
2526 .read(cx)
2527 .excerpt_containing(editor.newest_anchor_selection().head(), cx)
2528 });
2529 if let Some((excerpted_buffer, excerpt_range)) = excerpt {
2530 if excerpted_buffer == *buffer {
2531 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
2532 let excerpt_range = excerpt_range.to_offset(&snapshot);
2533 if snapshot
2534 .edited_ranges_for_transaction(transaction)
2535 .all(|range| {
2536 excerpt_range.start <= range.start && excerpt_range.end >= range.end
2537 })
2538 {
2539 return Ok(());
2540 }
2541 }
2542 }
2543 }
2544 } else {
2545 return Ok(());
2546 }
2547
2548 let mut ranges_to_highlight = Vec::new();
2549 let excerpt_buffer = cx.add_model(|cx| {
2550 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2551 for (buffer, transaction) in &entries {
2552 let snapshot = buffer.read(cx).snapshot();
2553 ranges_to_highlight.extend(
2554 multibuffer.push_excerpts_with_context_lines(
2555 buffer.clone(),
2556 snapshot
2557 .edited_ranges_for_transaction::<usize>(transaction)
2558 .collect(),
2559 1,
2560 cx,
2561 ),
2562 );
2563 }
2564 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2565 multibuffer
2566 });
2567
2568 workspace.update(&mut cx, |workspace, cx| {
2569 let project = workspace.project().clone();
2570 let editor =
2571 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2572 workspace.add_item(Box::new(editor.clone()), cx);
2573 editor.update(cx, |editor, cx| {
2574 editor.highlight_background::<Self>(
2575 ranges_to_highlight,
2576 |theme| theme.editor.highlighted_line_background,
2577 cx,
2578 );
2579 });
2580 });
2581
2582 Ok(())
2583 }
2584
2585 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2586 let project = self.project.as_ref()?;
2587 let buffer = self.buffer.read(cx);
2588 let newest_selection = self.newest_anchor_selection().clone();
2589 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2590 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2591 if start_buffer != end_buffer {
2592 return None;
2593 }
2594
2595 let actions = project.update(cx, |project, cx| {
2596 project.code_actions(&start_buffer, start..end, cx)
2597 });
2598 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2599 let actions = actions.await;
2600 if let Some(this) = this.upgrade(&cx) {
2601 this.update(&mut cx, |this, cx| {
2602 this.available_code_actions = actions.log_err().and_then(|actions| {
2603 if actions.is_empty() {
2604 None
2605 } else {
2606 Some((start_buffer, actions.into()))
2607 }
2608 });
2609 cx.notify();
2610 })
2611 }
2612 }));
2613 None
2614 }
2615
2616 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2617 if self.pending_rename.is_some() {
2618 return None;
2619 }
2620
2621 let project = self.project.as_ref()?;
2622 let buffer = self.buffer.read(cx);
2623 let newest_selection = self.newest_anchor_selection().clone();
2624 let cursor_position = newest_selection.head();
2625 let (cursor_buffer, cursor_buffer_position) =
2626 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2627 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2628 if cursor_buffer != tail_buffer {
2629 return None;
2630 }
2631
2632 let highlights = project.update(cx, |project, cx| {
2633 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2634 });
2635
2636 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2637 let highlights = highlights.log_err().await;
2638 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2639 this.update(&mut cx, |this, cx| {
2640 if this.pending_rename.is_some() {
2641 return;
2642 }
2643
2644 let buffer_id = cursor_position.buffer_id;
2645 let buffer = this.buffer.read(cx);
2646 if !buffer
2647 .text_anchor_for_position(cursor_position, cx)
2648 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2649 {
2650 return;
2651 }
2652
2653 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2654 let mut write_ranges = Vec::new();
2655 let mut read_ranges = Vec::new();
2656 for highlight in highlights {
2657 for (excerpt_id, excerpt_range) in
2658 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2659 {
2660 let start = highlight
2661 .range
2662 .start
2663 .max(&excerpt_range.start, cursor_buffer_snapshot);
2664 let end = highlight
2665 .range
2666 .end
2667 .min(&excerpt_range.end, cursor_buffer_snapshot);
2668 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2669 continue;
2670 }
2671
2672 let range = Anchor {
2673 buffer_id,
2674 excerpt_id: excerpt_id.clone(),
2675 text_anchor: start,
2676 }..Anchor {
2677 buffer_id,
2678 excerpt_id,
2679 text_anchor: end,
2680 };
2681 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2682 write_ranges.push(range);
2683 } else {
2684 read_ranges.push(range);
2685 }
2686 }
2687 }
2688
2689 this.highlight_background::<DocumentHighlightRead>(
2690 read_ranges,
2691 |theme| theme.editor.document_highlight_read_background,
2692 cx,
2693 );
2694 this.highlight_background::<DocumentHighlightWrite>(
2695 write_ranges,
2696 |theme| theme.editor.document_highlight_write_background,
2697 cx,
2698 );
2699 cx.notify();
2700 });
2701 }
2702 }));
2703 None
2704 }
2705
2706 pub fn render_code_actions_indicator(
2707 &self,
2708 style: &EditorStyle,
2709 cx: &mut ViewContext<Self>,
2710 ) -> Option<ElementBox> {
2711 if self.available_code_actions.is_some() {
2712 enum Tag {}
2713 Some(
2714 MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
2715 Svg::new("icons/zap.svg")
2716 .with_color(style.code_actions_indicator)
2717 .boxed()
2718 })
2719 .with_cursor_style(CursorStyle::PointingHand)
2720 .with_padding(Padding::uniform(3.))
2721 .on_mouse_down(|cx| {
2722 cx.dispatch_action(ToggleCodeActions {
2723 deployed_from_indicator: true,
2724 });
2725 })
2726 .boxed(),
2727 )
2728 } else {
2729 None
2730 }
2731 }
2732
2733 pub fn context_menu_visible(&self) -> bool {
2734 self.context_menu
2735 .as_ref()
2736 .map_or(false, |menu| menu.visible())
2737 }
2738
2739 pub fn render_context_menu(
2740 &self,
2741 cursor_position: DisplayPoint,
2742 style: EditorStyle,
2743 cx: &AppContext,
2744 ) -> Option<(DisplayPoint, ElementBox)> {
2745 self.context_menu
2746 .as_ref()
2747 .map(|menu| menu.render(cursor_position, style, cx))
2748 }
2749
2750 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
2751 if !matches!(menu, ContextMenu::Completions(_)) {
2752 self.completion_tasks.clear();
2753 }
2754 self.context_menu = Some(menu);
2755 cx.notify();
2756 }
2757
2758 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
2759 cx.notify();
2760 self.completion_tasks.clear();
2761 self.context_menu.take()
2762 }
2763
2764 pub fn insert_snippet(
2765 &mut self,
2766 insertion_ranges: &[Range<usize>],
2767 snippet: Snippet,
2768 cx: &mut ViewContext<Self>,
2769 ) -> Result<()> {
2770 let tabstops = self.buffer.update(cx, |buffer, cx| {
2771 let snippet_text: Arc<str> = snippet.text.clone().into();
2772 buffer.edit_with_autoindent(
2773 insertion_ranges
2774 .iter()
2775 .cloned()
2776 .map(|range| (range, snippet_text.clone())),
2777 cx,
2778 );
2779
2780 let snapshot = &*buffer.read(cx);
2781 let snippet = &snippet;
2782 snippet
2783 .tabstops
2784 .iter()
2785 .map(|tabstop| {
2786 let mut tabstop_ranges = tabstop
2787 .iter()
2788 .flat_map(|tabstop_range| {
2789 let mut delta = 0 as isize;
2790 insertion_ranges.iter().map(move |insertion_range| {
2791 let insertion_start = insertion_range.start as isize + delta;
2792 delta +=
2793 snippet.text.len() as isize - insertion_range.len() as isize;
2794
2795 let start = snapshot.anchor_before(
2796 (insertion_start + tabstop_range.start) as usize,
2797 );
2798 let end = snapshot
2799 .anchor_after((insertion_start + tabstop_range.end) as usize);
2800 start..end
2801 })
2802 })
2803 .collect::<Vec<_>>();
2804 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
2805 tabstop_ranges
2806 })
2807 .collect::<Vec<_>>()
2808 });
2809
2810 if let Some(tabstop) = tabstops.first() {
2811 self.select_ranges(tabstop.iter().cloned(), Some(Autoscroll::Fit), cx);
2812 self.snippet_stack.push(SnippetState {
2813 active_index: 0,
2814 ranges: tabstops,
2815 });
2816 }
2817
2818 Ok(())
2819 }
2820
2821 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2822 self.move_to_snippet_tabstop(Bias::Right, cx)
2823 }
2824
2825 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2826 self.move_to_snippet_tabstop(Bias::Left, cx)
2827 }
2828
2829 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
2830 let buffer = self.buffer.read(cx).snapshot(cx);
2831
2832 if let Some(snippet) = self.snippet_stack.last_mut() {
2833 match bias {
2834 Bias::Left => {
2835 if snippet.active_index > 0 {
2836 snippet.active_index -= 1;
2837 } else {
2838 return false;
2839 }
2840 }
2841 Bias::Right => {
2842 if snippet.active_index + 1 < snippet.ranges.len() {
2843 snippet.active_index += 1;
2844 } else {
2845 return false;
2846 }
2847 }
2848 }
2849 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
2850 let new_selections = current_ranges
2851 .iter()
2852 .map(|new_range| {
2853 let new_range = new_range.to_offset(&buffer);
2854 Selection {
2855 id: post_inc(&mut self.next_selection_id),
2856 start: new_range.start,
2857 end: new_range.end,
2858 reversed: false,
2859 goal: SelectionGoal::None,
2860 }
2861 })
2862 .collect();
2863
2864 // Remove the snippet state when moving to the last tabstop.
2865 if snippet.active_index + 1 == snippet.ranges.len() {
2866 self.snippet_stack.pop();
2867 }
2868
2869 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
2870 return true;
2871 }
2872 self.snippet_stack.pop();
2873 }
2874
2875 false
2876 }
2877
2878 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
2879 self.transact(cx, |this, cx| {
2880 this.select_all(&SelectAll, cx);
2881 this.insert("", cx);
2882 });
2883 }
2884
2885 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
2886 let mut selections = self.local_selections::<Point>(cx);
2887 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2888 for selection in &mut selections {
2889 if selection.is_empty() {
2890 let old_head = selection.head();
2891 let mut new_head =
2892 movement::left(&display_map, old_head.to_display_point(&display_map))
2893 .to_point(&display_map);
2894 if let Some((buffer, line_buffer_range)) = display_map
2895 .buffer_snapshot
2896 .buffer_line_for_row(old_head.row)
2897 {
2898 let indent_column = buffer.indent_column_for_line(line_buffer_range.start.row);
2899 let language_name = buffer.language().map(|language| language.name());
2900 let indent = cx.global::<Settings>().tab_size(language_name.as_deref());
2901 if old_head.column <= indent_column && old_head.column > 0 {
2902 new_head = cmp::min(
2903 new_head,
2904 Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
2905 );
2906 }
2907 }
2908
2909 selection.set_head(new_head, SelectionGoal::None);
2910 }
2911 }
2912
2913 self.transact(cx, |this, cx| {
2914 this.update_selections(selections, Some(Autoscroll::Fit), cx);
2915 this.insert("", cx);
2916 });
2917 }
2918
2919 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2920 self.transact(cx, |this, cx| {
2921 this.move_selections(cx, |map, selection| {
2922 if selection.is_empty() {
2923 let cursor = movement::right(map, selection.head());
2924 selection.set_head(cursor, SelectionGoal::None);
2925 }
2926 });
2927 this.insert(&"", cx);
2928 });
2929 }
2930
2931 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2932 if self.move_to_prev_snippet_tabstop(cx) {
2933 return;
2934 }
2935
2936 self.outdent(&Outdent, cx);
2937 }
2938
2939 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2940 if self.move_to_next_snippet_tabstop(cx) {
2941 return;
2942 }
2943
2944 let mut selections = self.local_selections::<Point>(cx);
2945 if selections.iter().all(|s| s.is_empty()) {
2946 self.transact(cx, |this, cx| {
2947 this.buffer.update(cx, |buffer, cx| {
2948 for selection in &mut selections {
2949 let language_name =
2950 buffer.language_at(selection.start, cx).map(|l| l.name());
2951 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2952 let char_column = buffer
2953 .read(cx)
2954 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
2955 .flat_map(str::chars)
2956 .count();
2957 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
2958 buffer.edit(
2959 [(
2960 selection.start..selection.start,
2961 " ".repeat(chars_to_next_tab_stop as usize),
2962 )],
2963 cx,
2964 );
2965 selection.start.column += chars_to_next_tab_stop;
2966 selection.end = selection.start;
2967 }
2968 });
2969 this.update_selections(selections, Some(Autoscroll::Fit), cx);
2970 });
2971 } else {
2972 self.indent(&Indent, cx);
2973 }
2974 }
2975
2976 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
2977 let mut selections = self.local_selections::<Point>(cx);
2978 self.transact(cx, |this, cx| {
2979 let mut last_indent = None;
2980 this.buffer.update(cx, |buffer, cx| {
2981 let snapshot = buffer.snapshot(cx);
2982 for selection in &mut selections {
2983 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2984 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2985 let mut start_row = selection.start.row;
2986 let mut end_row = selection.end.row + 1;
2987
2988 // If a selection ends at the beginning of a line, don't indent
2989 // that last line.
2990 if selection.end.column == 0 {
2991 end_row -= 1;
2992 }
2993
2994 // Avoid re-indenting a row that has already been indented by a
2995 // previous selection, but still update this selection's column
2996 // to reflect that indentation.
2997 if let Some((last_indent_row, last_indent_len)) = last_indent {
2998 if last_indent_row == selection.start.row {
2999 selection.start.column += last_indent_len;
3000 start_row += 1;
3001 }
3002 if last_indent_row == selection.end.row {
3003 selection.end.column += last_indent_len;
3004 }
3005 }
3006
3007 for row in start_row..end_row {
3008 let indent_column = snapshot.indent_column_for_line(row);
3009 let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
3010 let row_start = Point::new(row, 0);
3011 buffer.edit(
3012 [(
3013 row_start..row_start,
3014 " ".repeat(columns_to_next_tab_stop as usize),
3015 )],
3016 cx,
3017 );
3018
3019 // Update this selection's endpoints to reflect the indentation.
3020 if row == selection.start.row {
3021 selection.start.column += columns_to_next_tab_stop as u32;
3022 }
3023 if row == selection.end.row {
3024 selection.end.column += columns_to_next_tab_stop as u32;
3025 }
3026
3027 last_indent = Some((row, columns_to_next_tab_stop as u32));
3028 }
3029 }
3030 });
3031
3032 this.update_selections(selections, Some(Autoscroll::Fit), cx);
3033 });
3034 }
3035
3036 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3037 let selections = self.local_selections::<Point>(cx);
3038 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3039 let mut deletion_ranges = Vec::new();
3040 let mut last_outdent = None;
3041 {
3042 let buffer = self.buffer.read(cx);
3043 let snapshot = buffer.snapshot(cx);
3044 for selection in &selections {
3045 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3046 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
3047 let mut rows = selection.spanned_rows(false, &display_map);
3048
3049 // Avoid re-outdenting a row that has already been outdented by a
3050 // previous selection.
3051 if let Some(last_row) = last_outdent {
3052 if last_row == rows.start {
3053 rows.start += 1;
3054 }
3055 }
3056
3057 for row in rows {
3058 let column = snapshot.indent_column_for_line(row);
3059 if column > 0 {
3060 let mut deletion_len = column % tab_size;
3061 if deletion_len == 0 {
3062 deletion_len = tab_size;
3063 }
3064 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3065 last_outdent = Some(row);
3066 }
3067 }
3068 }
3069 }
3070
3071 self.transact(cx, |this, cx| {
3072 this.buffer.update(cx, |buffer, cx| {
3073 let empty_str: Arc<str> = "".into();
3074 buffer.edit(
3075 deletion_ranges
3076 .into_iter()
3077 .map(|range| (range, empty_str.clone())),
3078 cx,
3079 );
3080 });
3081 this.update_selections(
3082 this.local_selections::<usize>(cx),
3083 Some(Autoscroll::Fit),
3084 cx,
3085 );
3086 });
3087 }
3088
3089 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3090 let selections = self.local_selections::<Point>(cx);
3091 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3092 let buffer = self.buffer.read(cx).snapshot(cx);
3093
3094 let mut new_cursors = Vec::new();
3095 let mut edit_ranges = Vec::new();
3096 let mut selections = selections.iter().peekable();
3097 while let Some(selection) = selections.next() {
3098 let mut rows = selection.spanned_rows(false, &display_map);
3099 let goal_display_column = selection.head().to_display_point(&display_map).column();
3100
3101 // Accumulate contiguous regions of rows that we want to delete.
3102 while let Some(next_selection) = selections.peek() {
3103 let next_rows = next_selection.spanned_rows(false, &display_map);
3104 if next_rows.start <= rows.end {
3105 rows.end = next_rows.end;
3106 selections.next().unwrap();
3107 } else {
3108 break;
3109 }
3110 }
3111
3112 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3113 let edit_end;
3114 let cursor_buffer_row;
3115 if buffer.max_point().row >= rows.end {
3116 // If there's a line after the range, delete the \n from the end of the row range
3117 // and position the cursor on the next line.
3118 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3119 cursor_buffer_row = rows.end;
3120 } else {
3121 // If there isn't a line after the range, delete the \n from the line before the
3122 // start of the row range and position the cursor there.
3123 edit_start = edit_start.saturating_sub(1);
3124 edit_end = buffer.len();
3125 cursor_buffer_row = rows.start.saturating_sub(1);
3126 }
3127
3128 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3129 *cursor.column_mut() =
3130 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3131
3132 new_cursors.push((
3133 selection.id,
3134 buffer.anchor_after(cursor.to_point(&display_map)),
3135 ));
3136 edit_ranges.push(edit_start..edit_end);
3137 }
3138
3139 self.transact(cx, |this, cx| {
3140 let buffer = this.buffer.update(cx, |buffer, cx| {
3141 let empty_str: Arc<str> = "".into();
3142 buffer.edit(
3143 edit_ranges
3144 .into_iter()
3145 .map(|range| (range, empty_str.clone())),
3146 cx,
3147 );
3148 buffer.snapshot(cx)
3149 });
3150 let new_selections = new_cursors
3151 .into_iter()
3152 .map(|(id, cursor)| {
3153 let cursor = cursor.to_point(&buffer);
3154 Selection {
3155 id,
3156 start: cursor,
3157 end: cursor,
3158 reversed: false,
3159 goal: SelectionGoal::None,
3160 }
3161 })
3162 .collect();
3163 this.update_selections(new_selections, Some(Autoscroll::Fit), cx);
3164 });
3165 }
3166
3167 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3168 let selections = self.local_selections::<Point>(cx);
3169 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3170 let buffer = &display_map.buffer_snapshot;
3171
3172 let mut edits = Vec::new();
3173 let mut selections_iter = selections.iter().peekable();
3174 while let Some(selection) = selections_iter.next() {
3175 // Avoid duplicating the same lines twice.
3176 let mut rows = selection.spanned_rows(false, &display_map);
3177
3178 while let Some(next_selection) = selections_iter.peek() {
3179 let next_rows = next_selection.spanned_rows(false, &display_map);
3180 if next_rows.start <= rows.end - 1 {
3181 rows.end = next_rows.end;
3182 selections_iter.next().unwrap();
3183 } else {
3184 break;
3185 }
3186 }
3187
3188 // Copy the text from the selected row region and splice it at the start of the region.
3189 let start = Point::new(rows.start, 0);
3190 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3191 let text = buffer
3192 .text_for_range(start..end)
3193 .chain(Some("\n"))
3194 .collect::<String>();
3195 edits.push((start..start, text));
3196 }
3197
3198 self.transact(cx, |this, cx| {
3199 this.buffer.update(cx, |buffer, cx| {
3200 buffer.edit(edits, cx);
3201 });
3202
3203 this.request_autoscroll(Autoscroll::Fit, cx);
3204 });
3205 }
3206
3207 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3208 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3209 let buffer = self.buffer.read(cx).snapshot(cx);
3210
3211 let mut edits = Vec::new();
3212 let mut unfold_ranges = Vec::new();
3213 let mut refold_ranges = Vec::new();
3214
3215 let selections = self.local_selections::<Point>(cx);
3216 let mut selections = selections.iter().peekable();
3217 let mut contiguous_row_selections = Vec::new();
3218 let mut new_selections = Vec::new();
3219
3220 while let Some(selection) = selections.next() {
3221 // Find all the selections that span a contiguous row range
3222 contiguous_row_selections.push(selection.clone());
3223 let start_row = selection.start.row;
3224 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3225 display_map.next_line_boundary(selection.end).0.row + 1
3226 } else {
3227 selection.end.row
3228 };
3229
3230 while let Some(next_selection) = selections.peek() {
3231 if next_selection.start.row <= end_row {
3232 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3233 display_map.next_line_boundary(next_selection.end).0.row + 1
3234 } else {
3235 next_selection.end.row
3236 };
3237 contiguous_row_selections.push(selections.next().unwrap().clone());
3238 } else {
3239 break;
3240 }
3241 }
3242
3243 // Move the text spanned by the row range to be before the line preceding the row range
3244 if start_row > 0 {
3245 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3246 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3247 let insertion_point = display_map
3248 .prev_line_boundary(Point::new(start_row - 1, 0))
3249 .0;
3250
3251 // Don't move lines across excerpts
3252 if buffer
3253 .excerpt_boundaries_in_range((
3254 Bound::Excluded(insertion_point),
3255 Bound::Included(range_to_move.end),
3256 ))
3257 .next()
3258 .is_none()
3259 {
3260 let text = buffer
3261 .text_for_range(range_to_move.clone())
3262 .flat_map(|s| s.chars())
3263 .skip(1)
3264 .chain(['\n'])
3265 .collect::<String>();
3266
3267 edits.push((
3268 buffer.anchor_after(range_to_move.start)
3269 ..buffer.anchor_before(range_to_move.end),
3270 String::new(),
3271 ));
3272 let insertion_anchor = buffer.anchor_after(insertion_point);
3273 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3274
3275 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3276
3277 // Move selections up
3278 new_selections.extend(contiguous_row_selections.drain(..).map(
3279 |mut selection| {
3280 selection.start.row -= row_delta;
3281 selection.end.row -= row_delta;
3282 selection
3283 },
3284 ));
3285
3286 // Move folds up
3287 unfold_ranges.push(range_to_move.clone());
3288 for fold in display_map.folds_in_range(
3289 buffer.anchor_before(range_to_move.start)
3290 ..buffer.anchor_after(range_to_move.end),
3291 ) {
3292 let mut start = fold.start.to_point(&buffer);
3293 let mut end = fold.end.to_point(&buffer);
3294 start.row -= row_delta;
3295 end.row -= row_delta;
3296 refold_ranges.push(start..end);
3297 }
3298 }
3299 }
3300
3301 // If we didn't move line(s), preserve the existing selections
3302 new_selections.extend(contiguous_row_selections.drain(..));
3303 }
3304
3305 self.transact(cx, |this, cx| {
3306 this.unfold_ranges(unfold_ranges, true, cx);
3307 this.buffer.update(cx, |buffer, cx| {
3308 for (range, text) in edits {
3309 buffer.edit([(range, text)], cx);
3310 }
3311 });
3312 this.fold_ranges(refold_ranges, cx);
3313 this.update_selections(new_selections, Some(Autoscroll::Fit), cx);
3314 });
3315 }
3316
3317 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3318 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3319 let buffer = self.buffer.read(cx).snapshot(cx);
3320
3321 let mut edits = Vec::new();
3322 let mut unfold_ranges = Vec::new();
3323 let mut refold_ranges = Vec::new();
3324
3325 let selections = self.local_selections::<Point>(cx);
3326 let mut selections = selections.iter().peekable();
3327 let mut contiguous_row_selections = Vec::new();
3328 let mut new_selections = Vec::new();
3329
3330 while let Some(selection) = selections.next() {
3331 // Find all the selections that span a contiguous row range
3332 contiguous_row_selections.push(selection.clone());
3333 let start_row = selection.start.row;
3334 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3335 display_map.next_line_boundary(selection.end).0.row + 1
3336 } else {
3337 selection.end.row
3338 };
3339
3340 while let Some(next_selection) = selections.peek() {
3341 if next_selection.start.row <= end_row {
3342 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3343 display_map.next_line_boundary(next_selection.end).0.row + 1
3344 } else {
3345 next_selection.end.row
3346 };
3347 contiguous_row_selections.push(selections.next().unwrap().clone());
3348 } else {
3349 break;
3350 }
3351 }
3352
3353 // Move the text spanned by the row range to be after the last line of the row range
3354 if end_row <= buffer.max_point().row {
3355 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3356 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3357
3358 // Don't move lines across excerpt boundaries
3359 if buffer
3360 .excerpt_boundaries_in_range((
3361 Bound::Excluded(range_to_move.start),
3362 Bound::Included(insertion_point),
3363 ))
3364 .next()
3365 .is_none()
3366 {
3367 let mut text = String::from("\n");
3368 text.extend(buffer.text_for_range(range_to_move.clone()));
3369 text.pop(); // Drop trailing newline
3370 edits.push((
3371 buffer.anchor_after(range_to_move.start)
3372 ..buffer.anchor_before(range_to_move.end),
3373 String::new(),
3374 ));
3375 let insertion_anchor = buffer.anchor_after(insertion_point);
3376 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3377
3378 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3379
3380 // Move selections down
3381 new_selections.extend(contiguous_row_selections.drain(..).map(
3382 |mut selection| {
3383 selection.start.row += row_delta;
3384 selection.end.row += row_delta;
3385 selection
3386 },
3387 ));
3388
3389 // Move folds down
3390 unfold_ranges.push(range_to_move.clone());
3391 for fold in display_map.folds_in_range(
3392 buffer.anchor_before(range_to_move.start)
3393 ..buffer.anchor_after(range_to_move.end),
3394 ) {
3395 let mut start = fold.start.to_point(&buffer);
3396 let mut end = fold.end.to_point(&buffer);
3397 start.row += row_delta;
3398 end.row += row_delta;
3399 refold_ranges.push(start..end);
3400 }
3401 }
3402 }
3403
3404 // If we didn't move line(s), preserve the existing selections
3405 new_selections.extend(contiguous_row_selections.drain(..));
3406 }
3407
3408 self.transact(cx, |this, cx| {
3409 this.unfold_ranges(unfold_ranges, true, cx);
3410 this.buffer.update(cx, |buffer, cx| {
3411 for (range, text) in edits {
3412 buffer.edit([(range, text)], cx);
3413 }
3414 });
3415 this.fold_ranges(refold_ranges, cx);
3416 this.update_selections(new_selections, Some(Autoscroll::Fit), cx);
3417 });
3418 }
3419
3420 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3421 self.transact(cx, |this, cx| {
3422 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3423 this.move_selections(cx, |display_map, selection| {
3424 if !selection.is_empty() {
3425 return;
3426 }
3427
3428 let mut head = selection.head();
3429 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3430 if head.column() == display_map.line_len(head.row()) {
3431 transpose_offset = display_map
3432 .buffer_snapshot
3433 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3434 }
3435
3436 if transpose_offset == 0 {
3437 return;
3438 }
3439
3440 *head.column_mut() += 1;
3441 head = display_map.clip_point(head, Bias::Right);
3442 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3443
3444 let transpose_start = display_map
3445 .buffer_snapshot
3446 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3447 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3448 let transpose_end = display_map
3449 .buffer_snapshot
3450 .clip_offset(transpose_offset + 1, Bias::Right);
3451 if let Some(ch) = display_map.buffer_snapshot.chars_at(transpose_start).next() {
3452 edits.push((transpose_start..transpose_offset, String::new()));
3453 edits.push((transpose_end..transpose_end, ch.to_string()));
3454 }
3455 }
3456 });
3457 this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
3458 this.update_selections(
3459 this.local_selections::<usize>(cx),
3460 Some(Autoscroll::Fit),
3461 cx,
3462 );
3463 });
3464 }
3465
3466 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3467 let mut text = String::new();
3468 let mut selections = self.local_selections::<Point>(cx);
3469 let mut clipboard_selections = Vec::with_capacity(selections.len());
3470 {
3471 let buffer = self.buffer.read(cx).read(cx);
3472 let max_point = buffer.max_point();
3473 for selection in &mut selections {
3474 let is_entire_line = selection.is_empty();
3475 if is_entire_line {
3476 selection.start = Point::new(selection.start.row, 0);
3477 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3478 selection.goal = SelectionGoal::None;
3479 }
3480 let mut len = 0;
3481 for chunk in buffer.text_for_range(selection.start..selection.end) {
3482 text.push_str(chunk);
3483 len += chunk.len();
3484 }
3485 clipboard_selections.push(ClipboardSelection {
3486 len,
3487 is_entire_line,
3488 });
3489 }
3490 }
3491
3492 self.transact(cx, |this, cx| {
3493 this.update_selections(selections, Some(Autoscroll::Fit), cx);
3494 this.insert("", cx);
3495 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3496 });
3497 }
3498
3499 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3500 let selections = self.local_selections::<Point>(cx);
3501 let mut text = String::new();
3502 let mut clipboard_selections = Vec::with_capacity(selections.len());
3503 {
3504 let buffer = self.buffer.read(cx).read(cx);
3505 let max_point = buffer.max_point();
3506 for selection in selections.iter() {
3507 let mut start = selection.start;
3508 let mut end = selection.end;
3509 let is_entire_line = selection.is_empty();
3510 if is_entire_line {
3511 start = Point::new(start.row, 0);
3512 end = cmp::min(max_point, Point::new(start.row + 1, 0));
3513 }
3514 let mut len = 0;
3515 for chunk in buffer.text_for_range(start..end) {
3516 text.push_str(chunk);
3517 len += chunk.len();
3518 }
3519 clipboard_selections.push(ClipboardSelection {
3520 len,
3521 is_entire_line,
3522 });
3523 }
3524 }
3525
3526 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3527 }
3528
3529 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3530 self.transact(cx, |this, cx| {
3531 if let Some(item) = cx.as_mut().read_from_clipboard() {
3532 let mut clipboard_text = Cow::Borrowed(item.text());
3533 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3534 let old_selections = this.local_selections::<usize>(cx);
3535 let all_selections_were_entire_line =
3536 clipboard_selections.iter().all(|s| s.is_entire_line);
3537 if clipboard_selections.len() != old_selections.len() {
3538 let mut newline_separated_text = String::new();
3539 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3540 let mut ix = 0;
3541 while let Some(clipboard_selection) = clipboard_selections.next() {
3542 newline_separated_text
3543 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3544 ix += clipboard_selection.len;
3545 if clipboard_selections.peek().is_some() {
3546 newline_separated_text.push('\n');
3547 }
3548 }
3549 clipboard_text = Cow::Owned(newline_separated_text);
3550 }
3551
3552 this.buffer.update(cx, |buffer, cx| {
3553 let snapshot = buffer.read(cx);
3554 let mut start_offset = 0;
3555 let mut edits = Vec::new();
3556 for (ix, selection) in old_selections.iter().enumerate() {
3557 let to_insert;
3558 let entire_line;
3559 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3560 let end_offset = start_offset + clipboard_selection.len;
3561 to_insert = &clipboard_text[start_offset..end_offset];
3562 entire_line = clipboard_selection.is_entire_line;
3563 start_offset = end_offset;
3564 } else {
3565 to_insert = clipboard_text.as_str();
3566 entire_line = all_selections_were_entire_line;
3567 }
3568
3569 // If the corresponding selection was empty when this slice of the
3570 // clipboard text was written, then the entire line containing the
3571 // selection was copied. If this selection is also currently empty,
3572 // then paste the line before the current line of the buffer.
3573 let range = if selection.is_empty() && entire_line {
3574 let column = selection.start.to_point(&snapshot).column as usize;
3575 let line_start = selection.start - column;
3576 line_start..line_start
3577 } else {
3578 selection.start..selection.end
3579 };
3580
3581 edits.push((range, to_insert));
3582 }
3583 drop(snapshot);
3584 buffer.edit_with_autoindent(edits, cx);
3585 });
3586
3587 let selections = this.local_selections::<usize>(cx);
3588 this.update_selections(selections, Some(Autoscroll::Fit), cx);
3589 } else {
3590 this.insert(&clipboard_text, cx);
3591 }
3592 }
3593 });
3594 }
3595
3596 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3597 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3598 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3599 self.set_selections(selections, None, true, cx);
3600 }
3601 self.request_autoscroll(Autoscroll::Fit, cx);
3602 cx.emit(Event::Edited);
3603 }
3604 }
3605
3606 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3607 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3608 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3609 {
3610 self.set_selections(selections, None, true, cx);
3611 }
3612 self.request_autoscroll(Autoscroll::Fit, cx);
3613 cx.emit(Event::Edited);
3614 }
3615 }
3616
3617 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3618 self.buffer
3619 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3620 }
3621
3622 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3623 self.move_selections(cx, |map, selection| {
3624 let cursor = if selection.is_empty() {
3625 movement::left(map, selection.start)
3626 } else {
3627 selection.start
3628 };
3629 selection.collapse_to(cursor, SelectionGoal::None);
3630 });
3631 }
3632
3633 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3634 self.move_selection_heads(cx, |map, head, _| {
3635 (movement::left(map, head), SelectionGoal::None)
3636 });
3637 }
3638
3639 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3640 self.move_selections(cx, |map, selection| {
3641 let cursor = if selection.is_empty() {
3642 movement::right(map, selection.end)
3643 } else {
3644 selection.end
3645 };
3646 selection.collapse_to(cursor, SelectionGoal::None)
3647 });
3648 }
3649
3650 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3651 self.move_selection_heads(cx, |map, head, _| {
3652 (movement::right(map, head), SelectionGoal::None)
3653 });
3654 }
3655
3656 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3657 if self.take_rename(true, cx).is_some() {
3658 return;
3659 }
3660
3661 if let Some(context_menu) = self.context_menu.as_mut() {
3662 if context_menu.select_prev(cx) {
3663 return;
3664 }
3665 }
3666
3667 if matches!(self.mode, EditorMode::SingleLine) {
3668 cx.propagate_action();
3669 return;
3670 }
3671
3672 self.move_selections(cx, |map, selection| {
3673 if !selection.is_empty() {
3674 selection.goal = SelectionGoal::None;
3675 }
3676 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3677 selection.collapse_to(cursor, goal);
3678 });
3679 }
3680
3681 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3682 self.move_selection_heads(cx, |map, head, goal| movement::up(map, head, goal, false))
3683 }
3684
3685 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3686 self.take_rename(true, cx);
3687
3688 if let Some(context_menu) = self.context_menu.as_mut() {
3689 if context_menu.select_next(cx) {
3690 return;
3691 }
3692 }
3693
3694 if matches!(self.mode, EditorMode::SingleLine) {
3695 cx.propagate_action();
3696 return;
3697 }
3698
3699 self.move_selections(cx, |map, selection| {
3700 if !selection.is_empty() {
3701 selection.goal = SelectionGoal::None;
3702 }
3703 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3704 selection.collapse_to(cursor, goal);
3705 });
3706 }
3707
3708 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3709 self.move_selection_heads(cx, |map, head, goal| movement::down(map, head, goal, false))
3710 }
3711
3712 pub fn move_to_previous_word_start(
3713 &mut self,
3714 _: &MoveToPreviousWordStart,
3715 cx: &mut ViewContext<Self>,
3716 ) {
3717 self.move_cursors(cx, |map, head, _| {
3718 (
3719 movement::previous_word_start(map, head),
3720 SelectionGoal::None,
3721 )
3722 });
3723 }
3724
3725 pub fn move_to_previous_subword_start(
3726 &mut self,
3727 _: &MoveToPreviousSubwordStart,
3728 cx: &mut ViewContext<Self>,
3729 ) {
3730 self.move_cursors(cx, |map, head, _| {
3731 (
3732 movement::previous_subword_start(map, head),
3733 SelectionGoal::None,
3734 )
3735 });
3736 }
3737
3738 pub fn select_to_previous_word_start(
3739 &mut self,
3740 _: &SelectToPreviousWordStart,
3741 cx: &mut ViewContext<Self>,
3742 ) {
3743 self.move_selection_heads(cx, |map, head, _| {
3744 (
3745 movement::previous_word_start(map, head),
3746 SelectionGoal::None,
3747 )
3748 });
3749 }
3750
3751 pub fn select_to_previous_subword_start(
3752 &mut self,
3753 _: &SelectToPreviousSubwordStart,
3754 cx: &mut ViewContext<Self>,
3755 ) {
3756 self.move_selection_heads(cx, |map, head, _| {
3757 (
3758 movement::previous_subword_start(map, head),
3759 SelectionGoal::None,
3760 )
3761 });
3762 }
3763
3764 pub fn delete_to_previous_word_start(
3765 &mut self,
3766 _: &DeleteToPreviousWordStart,
3767 cx: &mut ViewContext<Self>,
3768 ) {
3769 self.transact(cx, |this, cx| {
3770 this.move_selections(cx, |map, selection| {
3771 if selection.is_empty() {
3772 let cursor = movement::previous_word_start(map, selection.head());
3773 selection.set_head(cursor, SelectionGoal::None);
3774 }
3775 });
3776 this.insert("", cx);
3777 });
3778 }
3779
3780 pub fn delete_to_previous_subword_start(
3781 &mut self,
3782 _: &DeleteToPreviousSubwordStart,
3783 cx: &mut ViewContext<Self>,
3784 ) {
3785 self.transact(cx, |this, cx| {
3786 this.move_selections(cx, |map, selection| {
3787 if selection.is_empty() {
3788 let cursor = movement::previous_subword_start(map, selection.head());
3789 selection.set_head(cursor, SelectionGoal::None);
3790 }
3791 });
3792 this.insert("", cx);
3793 });
3794 }
3795
3796 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3797 self.move_cursors(cx, |map, head, _| {
3798 (movement::next_word_end(map, head), SelectionGoal::None)
3799 });
3800 }
3801
3802 pub fn move_to_next_subword_end(
3803 &mut self,
3804 _: &MoveToNextSubwordEnd,
3805 cx: &mut ViewContext<Self>,
3806 ) {
3807 self.move_cursors(cx, |map, head, _| {
3808 (movement::next_subword_end(map, head), SelectionGoal::None)
3809 });
3810 }
3811
3812 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3813 self.move_selection_heads(cx, |map, head, _| {
3814 (movement::next_word_end(map, head), SelectionGoal::None)
3815 });
3816 }
3817
3818 pub fn select_to_next_subword_end(
3819 &mut self,
3820 _: &SelectToNextSubwordEnd,
3821 cx: &mut ViewContext<Self>,
3822 ) {
3823 self.move_selection_heads(cx, |map, head, _| {
3824 (movement::next_subword_end(map, head), SelectionGoal::None)
3825 });
3826 }
3827
3828 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3829 self.transact(cx, |this, cx| {
3830 this.move_selections(cx, |map, selection| {
3831 if selection.is_empty() {
3832 let cursor = movement::next_word_end(map, selection.head());
3833 selection.set_head(cursor, SelectionGoal::None);
3834 }
3835 });
3836 this.insert("", cx);
3837 });
3838 }
3839
3840 pub fn delete_to_next_subword_end(
3841 &mut self,
3842 _: &DeleteToNextSubwordEnd,
3843 cx: &mut ViewContext<Self>,
3844 ) {
3845 self.transact(cx, |this, cx| {
3846 this.move_selections(cx, |map, selection| {
3847 if selection.is_empty() {
3848 let cursor = movement::next_subword_end(map, selection.head());
3849 selection.set_head(cursor, SelectionGoal::None);
3850 }
3851 });
3852 this.insert("", cx);
3853 });
3854 }
3855
3856 pub fn move_to_beginning_of_line(
3857 &mut self,
3858 _: &MoveToBeginningOfLine,
3859 cx: &mut ViewContext<Self>,
3860 ) {
3861 self.move_cursors(cx, |map, head, _| {
3862 (
3863 movement::line_beginning(map, head, true),
3864 SelectionGoal::None,
3865 )
3866 });
3867 }
3868
3869 pub fn select_to_beginning_of_line(
3870 &mut self,
3871 action: &SelectToBeginningOfLine,
3872 cx: &mut ViewContext<Self>,
3873 ) {
3874 self.move_selection_heads(cx, |map, head, _| {
3875 (
3876 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3877 SelectionGoal::None,
3878 )
3879 });
3880 }
3881
3882 pub fn delete_to_beginning_of_line(
3883 &mut self,
3884 _: &DeleteToBeginningOfLine,
3885 cx: &mut ViewContext<Self>,
3886 ) {
3887 self.transact(cx, |this, cx| {
3888 this.move_selections(cx, |_, selection| {
3889 selection.reversed = true;
3890 });
3891
3892 this.select_to_beginning_of_line(
3893 &SelectToBeginningOfLine {
3894 stop_at_soft_wraps: false,
3895 },
3896 cx,
3897 );
3898 this.backspace(&Backspace, cx);
3899 });
3900 }
3901
3902 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
3903 self.move_cursors(cx, |map, head, _| {
3904 (movement::line_end(map, head, true), SelectionGoal::None)
3905 });
3906 }
3907
3908 pub fn select_to_end_of_line(
3909 &mut self,
3910 action: &SelectToEndOfLine,
3911 cx: &mut ViewContext<Self>,
3912 ) {
3913 self.move_selection_heads(cx, |map, head, _| {
3914 (
3915 movement::line_end(map, head, action.stop_at_soft_wraps),
3916 SelectionGoal::None,
3917 )
3918 });
3919 }
3920
3921 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
3922 self.transact(cx, |this, cx| {
3923 this.select_to_end_of_line(
3924 &SelectToEndOfLine {
3925 stop_at_soft_wraps: false,
3926 },
3927 cx,
3928 );
3929 this.delete(&Delete, cx);
3930 });
3931 }
3932
3933 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
3934 self.transact(cx, |this, cx| {
3935 this.select_to_end_of_line(
3936 &SelectToEndOfLine {
3937 stop_at_soft_wraps: false,
3938 },
3939 cx,
3940 );
3941 this.cut(&Cut, cx);
3942 });
3943 }
3944
3945 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
3946 if matches!(self.mode, EditorMode::SingleLine) {
3947 cx.propagate_action();
3948 return;
3949 }
3950
3951 let selection = Selection {
3952 id: post_inc(&mut self.next_selection_id),
3953 start: 0,
3954 end: 0,
3955 reversed: false,
3956 goal: SelectionGoal::None,
3957 };
3958 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3959 }
3960
3961 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
3962 let mut selection = self.local_selections::<Point>(cx).last().unwrap().clone();
3963 selection.set_head(Point::zero(), SelectionGoal::None);
3964 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3965 }
3966
3967 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
3968 if matches!(self.mode, EditorMode::SingleLine) {
3969 cx.propagate_action();
3970 return;
3971 }
3972
3973 let cursor = self.buffer.read(cx).read(cx).len();
3974 let selection = Selection {
3975 id: post_inc(&mut self.next_selection_id),
3976 start: cursor,
3977 end: cursor,
3978 reversed: false,
3979 goal: SelectionGoal::None,
3980 };
3981 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
3982 }
3983
3984 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
3985 self.nav_history = nav_history;
3986 }
3987
3988 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
3989 self.nav_history.as_ref()
3990 }
3991
3992 fn push_to_nav_history(
3993 &self,
3994 position: Anchor,
3995 new_position: Option<Point>,
3996 cx: &mut ViewContext<Self>,
3997 ) {
3998 if let Some(nav_history) = &self.nav_history {
3999 let buffer = self.buffer.read(cx).read(cx);
4000 let point = position.to_point(&buffer);
4001 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4002 drop(buffer);
4003
4004 if let Some(new_position) = new_position {
4005 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4006 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4007 return;
4008 }
4009 }
4010
4011 nav_history.push(Some(NavigationData {
4012 cursor_anchor: position,
4013 cursor_position: point,
4014 scroll_position: self.scroll_position,
4015 scroll_top_anchor: self.scroll_top_anchor.clone(),
4016 scroll_top_row,
4017 }));
4018 }
4019 }
4020
4021 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4022 let mut selection = self.local_selections::<usize>(cx).first().unwrap().clone();
4023 selection.set_head(self.buffer.read(cx).read(cx).len(), SelectionGoal::None);
4024 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
4025 }
4026
4027 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4028 let selection = Selection {
4029 id: post_inc(&mut self.next_selection_id),
4030 start: 0,
4031 end: self.buffer.read(cx).read(cx).len(),
4032 reversed: false,
4033 goal: SelectionGoal::None,
4034 };
4035 self.update_selections(vec![selection], None, cx);
4036 }
4037
4038 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4039 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4040 let mut selections = self.local_selections::<Point>(cx);
4041 let max_point = display_map.buffer_snapshot.max_point();
4042 for selection in &mut selections {
4043 let rows = selection.spanned_rows(true, &display_map);
4044 selection.start = Point::new(rows.start, 0);
4045 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4046 selection.reversed = false;
4047 }
4048 self.update_selections(selections, Some(Autoscroll::Fit), cx);
4049 }
4050
4051 pub fn split_selection_into_lines(
4052 &mut self,
4053 _: &SplitSelectionIntoLines,
4054 cx: &mut ViewContext<Self>,
4055 ) {
4056 let mut to_unfold = Vec::new();
4057 let mut new_selections = Vec::new();
4058 {
4059 let selections = self.local_selections::<Point>(cx);
4060 let buffer = self.buffer.read(cx).read(cx);
4061 for selection in selections {
4062 for row in selection.start.row..selection.end.row {
4063 let cursor = Point::new(row, buffer.line_len(row));
4064 new_selections.push(Selection {
4065 id: post_inc(&mut self.next_selection_id),
4066 start: cursor,
4067 end: cursor,
4068 reversed: false,
4069 goal: SelectionGoal::None,
4070 });
4071 }
4072 new_selections.push(Selection {
4073 id: selection.id,
4074 start: selection.end,
4075 end: selection.end,
4076 reversed: false,
4077 goal: SelectionGoal::None,
4078 });
4079 to_unfold.push(selection.start..selection.end);
4080 }
4081 }
4082 self.unfold_ranges(to_unfold, true, cx);
4083 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
4084 }
4085
4086 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4087 self.add_selection(true, cx);
4088 }
4089
4090 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4091 self.add_selection(false, cx);
4092 }
4093
4094 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4095 self.push_to_selection_history();
4096 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4097 let mut selections = self.local_selections::<Point>(cx);
4098 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4099 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4100 let range = oldest_selection.display_range(&display_map).sorted();
4101 let columns = cmp::min(range.start.column(), range.end.column())
4102 ..cmp::max(range.start.column(), range.end.column());
4103
4104 selections.clear();
4105 let mut stack = Vec::new();
4106 for row in range.start.row()..=range.end.row() {
4107 if let Some(selection) = self.build_columnar_selection(
4108 &display_map,
4109 row,
4110 &columns,
4111 oldest_selection.reversed,
4112 ) {
4113 stack.push(selection.id);
4114 selections.push(selection);
4115 }
4116 }
4117
4118 if above {
4119 stack.reverse();
4120 }
4121
4122 AddSelectionsState { above, stack }
4123 });
4124
4125 let last_added_selection = *state.stack.last().unwrap();
4126 let mut new_selections = Vec::new();
4127 if above == state.above {
4128 let end_row = if above {
4129 0
4130 } else {
4131 display_map.max_point().row()
4132 };
4133
4134 'outer: for selection in selections {
4135 if selection.id == last_added_selection {
4136 let range = selection.display_range(&display_map).sorted();
4137 debug_assert_eq!(range.start.row(), range.end.row());
4138 let mut row = range.start.row();
4139 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4140 {
4141 start..end
4142 } else {
4143 cmp::min(range.start.column(), range.end.column())
4144 ..cmp::max(range.start.column(), range.end.column())
4145 };
4146
4147 while row != end_row {
4148 if above {
4149 row -= 1;
4150 } else {
4151 row += 1;
4152 }
4153
4154 if let Some(new_selection) = self.build_columnar_selection(
4155 &display_map,
4156 row,
4157 &columns,
4158 selection.reversed,
4159 ) {
4160 state.stack.push(new_selection.id);
4161 if above {
4162 new_selections.push(new_selection);
4163 new_selections.push(selection);
4164 } else {
4165 new_selections.push(selection);
4166 new_selections.push(new_selection);
4167 }
4168
4169 continue 'outer;
4170 }
4171 }
4172 }
4173
4174 new_selections.push(selection);
4175 }
4176 } else {
4177 new_selections = selections;
4178 new_selections.retain(|s| s.id != last_added_selection);
4179 state.stack.pop();
4180 }
4181
4182 self.update_selections(new_selections, Some(Autoscroll::Newest), cx);
4183 if state.stack.len() > 1 {
4184 self.add_selections_state = Some(state);
4185 }
4186 }
4187
4188 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4189 self.push_to_selection_history();
4190 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4191 let buffer = &display_map.buffer_snapshot;
4192 let mut selections = self.local_selections::<usize>(cx);
4193 if let Some(mut select_next_state) = self.select_next_state.take() {
4194 let query = &select_next_state.query;
4195 if !select_next_state.done {
4196 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4197 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4198 let mut next_selected_range = None;
4199
4200 let bytes_after_last_selection =
4201 buffer.bytes_in_range(last_selection.end..buffer.len());
4202 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4203 let query_matches = query
4204 .stream_find_iter(bytes_after_last_selection)
4205 .map(|result| (last_selection.end, result))
4206 .chain(
4207 query
4208 .stream_find_iter(bytes_before_first_selection)
4209 .map(|result| (0, result)),
4210 );
4211 for (start_offset, query_match) in query_matches {
4212 let query_match = query_match.unwrap(); // can only fail due to I/O
4213 let offset_range =
4214 start_offset + query_match.start()..start_offset + query_match.end();
4215 let display_range = offset_range.start.to_display_point(&display_map)
4216 ..offset_range.end.to_display_point(&display_map);
4217
4218 if !select_next_state.wordwise
4219 || (!movement::is_inside_word(&display_map, display_range.start)
4220 && !movement::is_inside_word(&display_map, display_range.end))
4221 {
4222 next_selected_range = Some(offset_range);
4223 break;
4224 }
4225 }
4226
4227 if let Some(next_selected_range) = next_selected_range {
4228 if action.replace_newest {
4229 if let Some(newest_id) =
4230 selections.iter().max_by_key(|s| s.id).map(|s| s.id)
4231 {
4232 selections.retain(|s| s.id != newest_id);
4233 }
4234 }
4235 selections.push(Selection {
4236 id: post_inc(&mut self.next_selection_id),
4237 start: next_selected_range.start,
4238 end: next_selected_range.end,
4239 reversed: false,
4240 goal: SelectionGoal::None,
4241 });
4242 self.unfold_ranges([next_selected_range], false, cx);
4243 self.update_selections(selections, Some(Autoscroll::Newest), cx);
4244 } else {
4245 select_next_state.done = true;
4246 }
4247 }
4248
4249 self.select_next_state = Some(select_next_state);
4250 } else if selections.len() == 1 {
4251 let selection = selections.last_mut().unwrap();
4252 if selection.start == selection.end {
4253 let word_range = movement::surrounding_word(
4254 &display_map,
4255 selection.start.to_display_point(&display_map),
4256 );
4257 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4258 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4259 selection.goal = SelectionGoal::None;
4260 selection.reversed = false;
4261
4262 let query = buffer
4263 .text_for_range(selection.start..selection.end)
4264 .collect::<String>();
4265 let select_state = SelectNextState {
4266 query: AhoCorasick::new_auto_configured(&[query]),
4267 wordwise: true,
4268 done: false,
4269 };
4270 self.unfold_ranges([selection.start..selection.end], false, cx);
4271 self.update_selections(selections, Some(Autoscroll::Newest), cx);
4272 self.select_next_state = Some(select_state);
4273 } else {
4274 let query = buffer
4275 .text_for_range(selection.start..selection.end)
4276 .collect::<String>();
4277 self.select_next_state = Some(SelectNextState {
4278 query: AhoCorasick::new_auto_configured(&[query]),
4279 wordwise: false,
4280 done: false,
4281 });
4282 self.select_next(action, cx);
4283 }
4284 }
4285 }
4286
4287 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4288 self.transact(cx, |this, cx| {
4289 let mut selections = this.local_selections::<Point>(cx);
4290 let mut all_selection_lines_are_comments = true;
4291 let mut edit_ranges = Vec::new();
4292 let mut last_toggled_row = None;
4293 this.buffer.update(cx, |buffer, cx| {
4294 // TODO: Handle selections that cross excerpts
4295 for selection in &mut selections {
4296 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4297 // as that portion won't be used for detecting if a line is a comment.
4298 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4299 .language_at(selection.start, cx)
4300 .and_then(|l| l.line_comment_prefix())
4301 {
4302 prefix.into()
4303 } else {
4304 return;
4305 };
4306 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4307 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4308 edit_ranges.clear();
4309 let snapshot = buffer.snapshot(cx);
4310
4311 let end_row =
4312 if selection.end.row > selection.start.row && selection.end.column == 0 {
4313 selection.end.row
4314 } else {
4315 selection.end.row + 1
4316 };
4317
4318 for row in selection.start.row..end_row {
4319 // If multiple selections contain a given row, avoid processing that
4320 // row more than once.
4321 if last_toggled_row == Some(row) {
4322 continue;
4323 } else {
4324 last_toggled_row = Some(row);
4325 }
4326
4327 if snapshot.is_line_blank(row) {
4328 continue;
4329 }
4330
4331 let start = Point::new(row, snapshot.indent_column_for_line(row));
4332 let mut line_bytes = snapshot
4333 .bytes_in_range(start..snapshot.max_point())
4334 .flatten()
4335 .copied();
4336
4337 // If this line currently begins with the line comment prefix, then record
4338 // the range containing the prefix.
4339 if all_selection_lines_are_comments
4340 && line_bytes
4341 .by_ref()
4342 .take(comment_prefix.len())
4343 .eq(comment_prefix.bytes())
4344 {
4345 // Include any whitespace that matches the comment prefix.
4346 let matching_whitespace_len = line_bytes
4347 .zip(comment_prefix_whitespace.bytes())
4348 .take_while(|(a, b)| a == b)
4349 .count()
4350 as u32;
4351 let end = Point::new(
4352 row,
4353 start.column
4354 + comment_prefix.len() as u32
4355 + matching_whitespace_len,
4356 );
4357 edit_ranges.push(start..end);
4358 }
4359 // If this line does not begin with the line comment prefix, then record
4360 // the position where the prefix should be inserted.
4361 else {
4362 all_selection_lines_are_comments = false;
4363 edit_ranges.push(start..start);
4364 }
4365 }
4366
4367 if !edit_ranges.is_empty() {
4368 if all_selection_lines_are_comments {
4369 let empty_str: Arc<str> = "".into();
4370 buffer.edit(
4371 edit_ranges
4372 .iter()
4373 .cloned()
4374 .map(|range| (range, empty_str.clone())),
4375 cx,
4376 );
4377 } else {
4378 let min_column =
4379 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4380 let edits = edit_ranges.iter().map(|range| {
4381 let position = Point::new(range.start.row, min_column);
4382 (position..position, full_comment_prefix.clone())
4383 });
4384 buffer.edit(edits, cx);
4385 }
4386 }
4387 }
4388 });
4389
4390 this.update_selections(
4391 this.local_selections::<usize>(cx),
4392 Some(Autoscroll::Fit),
4393 cx,
4394 );
4395 });
4396 }
4397
4398 pub fn select_larger_syntax_node(
4399 &mut self,
4400 _: &SelectLargerSyntaxNode,
4401 cx: &mut ViewContext<Self>,
4402 ) {
4403 let old_selections = self.local_selections::<usize>(cx).into_boxed_slice();
4404 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4405 let buffer = self.buffer.read(cx).snapshot(cx);
4406
4407 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4408 let mut selected_larger_node = false;
4409 let new_selections = old_selections
4410 .iter()
4411 .map(|selection| {
4412 let old_range = selection.start..selection.end;
4413 let mut new_range = old_range.clone();
4414 while let Some(containing_range) =
4415 buffer.range_for_syntax_ancestor(new_range.clone())
4416 {
4417 new_range = containing_range;
4418 if !display_map.intersects_fold(new_range.start)
4419 && !display_map.intersects_fold(new_range.end)
4420 {
4421 break;
4422 }
4423 }
4424
4425 selected_larger_node |= new_range != old_range;
4426 Selection {
4427 id: selection.id,
4428 start: new_range.start,
4429 end: new_range.end,
4430 goal: SelectionGoal::None,
4431 reversed: selection.reversed,
4432 }
4433 })
4434 .collect::<Vec<_>>();
4435
4436 if selected_larger_node {
4437 stack.push(old_selections);
4438 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
4439 }
4440 self.select_larger_syntax_node_stack = stack;
4441 }
4442
4443 pub fn select_smaller_syntax_node(
4444 &mut self,
4445 _: &SelectSmallerSyntaxNode,
4446 cx: &mut ViewContext<Self>,
4447 ) {
4448 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4449 if let Some(selections) = stack.pop() {
4450 self.update_selections(selections.to_vec(), Some(Autoscroll::Fit), cx);
4451 }
4452 self.select_larger_syntax_node_stack = stack;
4453 }
4454
4455 pub fn move_to_enclosing_bracket(
4456 &mut self,
4457 _: &MoveToEnclosingBracket,
4458 cx: &mut ViewContext<Self>,
4459 ) {
4460 let mut selections = self.local_selections::<usize>(cx);
4461 let buffer = self.buffer.read(cx).snapshot(cx);
4462 for selection in &mut selections {
4463 if let Some((open_range, close_range)) =
4464 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4465 {
4466 let close_range = close_range.to_inclusive();
4467 let destination = if close_range.contains(&selection.start)
4468 && close_range.contains(&selection.end)
4469 {
4470 open_range.end
4471 } else {
4472 *close_range.start()
4473 };
4474 selection.start = destination;
4475 selection.end = destination;
4476 }
4477 }
4478
4479 self.update_selections(selections, Some(Autoscroll::Fit), cx);
4480 }
4481
4482 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4483 self.end_selection(cx);
4484 self.selection_history.mode = SelectionHistoryMode::Undoing;
4485 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4486 self.set_selections(entry.selections, None, true, cx);
4487 self.select_next_state = entry.select_next_state;
4488 self.add_selections_state = entry.add_selections_state;
4489 self.request_autoscroll(Autoscroll::Newest, cx);
4490 }
4491 self.selection_history.mode = SelectionHistoryMode::Normal;
4492 }
4493
4494 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4495 self.end_selection(cx);
4496 self.selection_history.mode = SelectionHistoryMode::Redoing;
4497 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4498 self.set_selections(entry.selections, None, true, cx);
4499 self.select_next_state = entry.select_next_state;
4500 self.add_selections_state = entry.add_selections_state;
4501 self.request_autoscroll(Autoscroll::Newest, cx);
4502 }
4503 self.selection_history.mode = SelectionHistoryMode::Normal;
4504 }
4505
4506 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4507 self.go_to_diagnostic(Direction::Next, cx)
4508 }
4509
4510 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4511 self.go_to_diagnostic(Direction::Prev, cx)
4512 }
4513
4514 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4515 let buffer = self.buffer.read(cx).snapshot(cx);
4516 let selection = self.newest_selection_with_snapshot::<usize>(&buffer);
4517 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4518 active_diagnostics
4519 .primary_range
4520 .to_offset(&buffer)
4521 .to_inclusive()
4522 });
4523 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4524 if active_primary_range.contains(&selection.head()) {
4525 *active_primary_range.end()
4526 } else {
4527 selection.head()
4528 }
4529 } else {
4530 selection.head()
4531 };
4532
4533 loop {
4534 let mut diagnostics = if direction == Direction::Prev {
4535 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4536 } else {
4537 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4538 };
4539 let group = diagnostics.find_map(|entry| {
4540 if entry.diagnostic.is_primary
4541 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4542 && !entry.range.is_empty()
4543 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4544 {
4545 Some((entry.range, entry.diagnostic.group_id))
4546 } else {
4547 None
4548 }
4549 });
4550
4551 if let Some((primary_range, group_id)) = group {
4552 self.activate_diagnostics(group_id, cx);
4553 self.update_selections(
4554 vec![Selection {
4555 id: selection.id,
4556 start: primary_range.start,
4557 end: primary_range.start,
4558 reversed: false,
4559 goal: SelectionGoal::None,
4560 }],
4561 Some(Autoscroll::Center),
4562 cx,
4563 );
4564 break;
4565 } else {
4566 // Cycle around to the start of the buffer, potentially moving back to the start of
4567 // the currently active diagnostic.
4568 active_primary_range.take();
4569 if direction == Direction::Prev {
4570 if search_start == buffer.len() {
4571 break;
4572 } else {
4573 search_start = buffer.len();
4574 }
4575 } else {
4576 if search_start == 0 {
4577 break;
4578 } else {
4579 search_start = 0;
4580 }
4581 }
4582 }
4583 }
4584 }
4585
4586 pub fn go_to_definition(
4587 workspace: &mut Workspace,
4588 _: &GoToDefinition,
4589 cx: &mut ViewContext<Workspace>,
4590 ) {
4591 let active_item = workspace.active_item(cx);
4592 let editor_handle = if let Some(editor) = active_item
4593 .as_ref()
4594 .and_then(|item| item.act_as::<Self>(cx))
4595 {
4596 editor
4597 } else {
4598 return;
4599 };
4600
4601 let editor = editor_handle.read(cx);
4602 let head = editor.newest_selection::<usize>(cx).head();
4603 let (buffer, head) =
4604 if let Some(text_anchor) = editor.buffer.read(cx).text_anchor_for_position(head, cx) {
4605 text_anchor
4606 } else {
4607 return;
4608 };
4609
4610 let project = workspace.project().clone();
4611 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4612 cx.spawn(|workspace, mut cx| async move {
4613 let definitions = definitions.await?;
4614 workspace.update(&mut cx, |workspace, cx| {
4615 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4616 for definition in definitions {
4617 let range = definition.range.to_offset(definition.buffer.read(cx));
4618
4619 let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
4620 target_editor_handle.update(cx, |target_editor, cx| {
4621 // When selecting a definition in a different buffer, disable the nav history
4622 // to avoid creating a history entry at the previous cursor location.
4623 if editor_handle != target_editor_handle {
4624 nav_history.borrow_mut().disable();
4625 }
4626 target_editor.select_ranges([range], Some(Autoscroll::Center), cx);
4627 nav_history.borrow_mut().enable();
4628 });
4629 }
4630 });
4631
4632 Ok::<(), anyhow::Error>(())
4633 })
4634 .detach_and_log_err(cx);
4635 }
4636
4637 pub fn find_all_references(
4638 workspace: &mut Workspace,
4639 _: &FindAllReferences,
4640 cx: &mut ViewContext<Workspace>,
4641 ) -> Option<Task<Result<()>>> {
4642 let active_item = workspace.active_item(cx)?;
4643 let editor_handle = active_item.act_as::<Self>(cx)?;
4644
4645 let editor = editor_handle.read(cx);
4646 let head = editor.newest_selection::<usize>(cx).head();
4647 let (buffer, head) = editor.buffer.read(cx).text_anchor_for_position(head, cx)?;
4648 let replica_id = editor.replica_id(cx);
4649
4650 let project = workspace.project().clone();
4651 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4652 Some(cx.spawn(|workspace, mut cx| async move {
4653 let mut locations = references.await?;
4654 if locations.is_empty() {
4655 return Ok(());
4656 }
4657
4658 locations.sort_by_key(|location| location.buffer.id());
4659 let mut locations = locations.into_iter().peekable();
4660 let mut ranges_to_highlight = Vec::new();
4661
4662 let excerpt_buffer = cx.add_model(|cx| {
4663 let mut symbol_name = None;
4664 let mut multibuffer = MultiBuffer::new(replica_id);
4665 while let Some(location) = locations.next() {
4666 let buffer = location.buffer.read(cx);
4667 let mut ranges_for_buffer = Vec::new();
4668 let range = location.range.to_offset(buffer);
4669 ranges_for_buffer.push(range.clone());
4670 if symbol_name.is_none() {
4671 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4672 }
4673
4674 while let Some(next_location) = locations.peek() {
4675 if next_location.buffer == location.buffer {
4676 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4677 locations.next();
4678 } else {
4679 break;
4680 }
4681 }
4682
4683 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4684 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4685 location.buffer.clone(),
4686 ranges_for_buffer,
4687 1,
4688 cx,
4689 ));
4690 }
4691 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4692 });
4693
4694 workspace.update(&mut cx, |workspace, cx| {
4695 let editor =
4696 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4697 editor.update(cx, |editor, cx| {
4698 editor.highlight_background::<Self>(
4699 ranges_to_highlight,
4700 |theme| theme.editor.highlighted_line_background,
4701 cx,
4702 );
4703 });
4704 workspace.add_item(Box::new(editor), cx);
4705 });
4706
4707 Ok(())
4708 }))
4709 }
4710
4711 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4712 use language::ToOffset as _;
4713
4714 let project = self.project.clone()?;
4715 let selection = self.newest_anchor_selection().clone();
4716 let (cursor_buffer, cursor_buffer_position) = self
4717 .buffer
4718 .read(cx)
4719 .text_anchor_for_position(selection.head(), cx)?;
4720 let (tail_buffer, _) = self
4721 .buffer
4722 .read(cx)
4723 .text_anchor_for_position(selection.tail(), cx)?;
4724 if tail_buffer != cursor_buffer {
4725 return None;
4726 }
4727
4728 let snapshot = cursor_buffer.read(cx).snapshot();
4729 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4730 let prepare_rename = project.update(cx, |project, cx| {
4731 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4732 });
4733
4734 Some(cx.spawn(|this, mut cx| async move {
4735 let rename_range = if let Some(range) = prepare_rename.await? {
4736 Some(range)
4737 } else {
4738 this.read_with(&cx, |this, cx| {
4739 let buffer = this.buffer.read(cx).snapshot(cx);
4740 let mut buffer_highlights = this
4741 .document_highlights_for_position(selection.head(), &buffer)
4742 .filter(|highlight| {
4743 highlight.start.excerpt_id() == selection.head().excerpt_id()
4744 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4745 });
4746 buffer_highlights
4747 .next()
4748 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4749 })
4750 };
4751 if let Some(rename_range) = rename_range {
4752 let rename_buffer_range = rename_range.to_offset(&snapshot);
4753 let cursor_offset_in_rename_range =
4754 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4755
4756 this.update(&mut cx, |this, cx| {
4757 this.take_rename(false, cx);
4758 let style = this.style(cx);
4759 let buffer = this.buffer.read(cx).read(cx);
4760 let cursor_offset = selection.head().to_offset(&buffer);
4761 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4762 let rename_end = rename_start + rename_buffer_range.len();
4763 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4764 let mut old_highlight_id = None;
4765 let old_name: Arc<str> = buffer
4766 .chunks(rename_start..rename_end, true)
4767 .map(|chunk| {
4768 if old_highlight_id.is_none() {
4769 old_highlight_id = chunk.syntax_highlight_id;
4770 }
4771 chunk.text
4772 })
4773 .collect::<String>()
4774 .into();
4775
4776 drop(buffer);
4777
4778 // Position the selection in the rename editor so that it matches the current selection.
4779 this.show_local_selections = false;
4780 let rename_editor = cx.add_view(|cx| {
4781 let mut editor = Editor::single_line(None, cx);
4782 if let Some(old_highlight_id) = old_highlight_id {
4783 editor.override_text_style =
4784 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4785 }
4786 editor
4787 .buffer
4788 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4789 editor.select_all(&SelectAll, cx);
4790 editor
4791 });
4792
4793 let ranges = this
4794 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4795 .into_iter()
4796 .flat_map(|(_, ranges)| ranges)
4797 .chain(
4798 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4799 .into_iter()
4800 .flat_map(|(_, ranges)| ranges),
4801 )
4802 .collect();
4803
4804 this.highlight_text::<Rename>(
4805 ranges,
4806 HighlightStyle {
4807 fade_out: Some(style.rename_fade),
4808 ..Default::default()
4809 },
4810 cx,
4811 );
4812 cx.focus(&rename_editor);
4813 let block_id = this.insert_blocks(
4814 [BlockProperties {
4815 position: range.start.clone(),
4816 height: 1,
4817 render: Arc::new({
4818 let editor = rename_editor.clone();
4819 move |cx: &BlockContext| {
4820 ChildView::new(editor.clone())
4821 .contained()
4822 .with_padding_left(cx.anchor_x)
4823 .boxed()
4824 }
4825 }),
4826 disposition: BlockDisposition::Below,
4827 }],
4828 cx,
4829 )[0];
4830 this.pending_rename = Some(RenameState {
4831 range,
4832 old_name,
4833 editor: rename_editor,
4834 block_id,
4835 });
4836 });
4837 }
4838
4839 Ok(())
4840 }))
4841 }
4842
4843 pub fn confirm_rename(
4844 workspace: &mut Workspace,
4845 _: &ConfirmRename,
4846 cx: &mut ViewContext<Workspace>,
4847 ) -> Option<Task<Result<()>>> {
4848 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4849
4850 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4851 let rename = editor.take_rename(false, cx)?;
4852 let buffer = editor.buffer.read(cx);
4853 let (start_buffer, start) =
4854 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4855 let (end_buffer, end) =
4856 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4857 if start_buffer == end_buffer {
4858 let new_name = rename.editor.read(cx).text(cx);
4859 Some((start_buffer, start..end, rename.old_name, new_name))
4860 } else {
4861 None
4862 }
4863 })?;
4864
4865 let rename = workspace.project().clone().update(cx, |project, cx| {
4866 project.perform_rename(
4867 buffer.clone(),
4868 range.start.clone(),
4869 new_name.clone(),
4870 true,
4871 cx,
4872 )
4873 });
4874
4875 Some(cx.spawn(|workspace, mut cx| async move {
4876 let project_transaction = rename.await?;
4877 Self::open_project_transaction(
4878 editor.clone(),
4879 workspace,
4880 project_transaction,
4881 format!("Rename: {} → {}", old_name, new_name),
4882 cx.clone(),
4883 )
4884 .await?;
4885
4886 editor.update(&mut cx, |editor, cx| {
4887 editor.refresh_document_highlights(cx);
4888 });
4889 Ok(())
4890 }))
4891 }
4892
4893 fn take_rename(
4894 &mut self,
4895 moving_cursor: bool,
4896 cx: &mut ViewContext<Self>,
4897 ) -> Option<RenameState> {
4898 let rename = self.pending_rename.take()?;
4899 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4900 self.clear_text_highlights::<Rename>(cx);
4901 self.show_local_selections = true;
4902
4903 if moving_cursor {
4904 let cursor_in_rename_editor =
4905 rename.editor.read(cx).newest_selection::<usize>(cx).head();
4906
4907 // Update the selection to match the position of the selection inside
4908 // the rename editor.
4909 let snapshot = self.buffer.read(cx).read(cx);
4910 let rename_range = rename.range.to_offset(&snapshot);
4911 let cursor_in_editor = snapshot
4912 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4913 .min(rename_range.end);
4914 drop(snapshot);
4915
4916 self.update_selections(
4917 vec![Selection {
4918 id: self.newest_anchor_selection().id,
4919 start: cursor_in_editor,
4920 end: cursor_in_editor,
4921 reversed: false,
4922 goal: SelectionGoal::None,
4923 }],
4924 None,
4925 cx,
4926 );
4927 }
4928
4929 Some(rename)
4930 }
4931
4932 #[cfg(any(test, feature = "test-support"))]
4933 pub fn pending_rename(&self) -> Option<&RenameState> {
4934 self.pending_rename.as_ref()
4935 }
4936
4937 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4938 if let Some(project) = self.project.clone() {
4939 self.buffer.update(cx, |multi_buffer, cx| {
4940 project.update(cx, |project, cx| {
4941 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
4942 });
4943 })
4944 }
4945 }
4946
4947 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
4948 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
4949 let buffer = self.buffer.read(cx).snapshot(cx);
4950 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
4951 let is_valid = buffer
4952 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
4953 .any(|entry| {
4954 entry.diagnostic.is_primary
4955 && !entry.range.is_empty()
4956 && entry.range.start == primary_range_start
4957 && entry.diagnostic.message == active_diagnostics.primary_message
4958 });
4959
4960 if is_valid != active_diagnostics.is_valid {
4961 active_diagnostics.is_valid = is_valid;
4962 let mut new_styles = HashMap::default();
4963 for (block_id, diagnostic) in &active_diagnostics.blocks {
4964 new_styles.insert(
4965 *block_id,
4966 diagnostic_block_renderer(diagnostic.clone(), is_valid),
4967 );
4968 }
4969 self.display_map
4970 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
4971 }
4972 }
4973 }
4974
4975 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
4976 self.dismiss_diagnostics(cx);
4977 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
4978 let buffer = self.buffer.read(cx).snapshot(cx);
4979
4980 let mut primary_range = None;
4981 let mut primary_message = None;
4982 let mut group_end = Point::zero();
4983 let diagnostic_group = buffer
4984 .diagnostic_group::<Point>(group_id)
4985 .map(|entry| {
4986 if entry.range.end > group_end {
4987 group_end = entry.range.end;
4988 }
4989 if entry.diagnostic.is_primary {
4990 primary_range = Some(entry.range.clone());
4991 primary_message = Some(entry.diagnostic.message.clone());
4992 }
4993 entry
4994 })
4995 .collect::<Vec<_>>();
4996 let primary_range = primary_range.unwrap();
4997 let primary_message = primary_message.unwrap();
4998 let primary_range =
4999 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5000
5001 let blocks = display_map
5002 .insert_blocks(
5003 diagnostic_group.iter().map(|entry| {
5004 let diagnostic = entry.diagnostic.clone();
5005 let message_height = diagnostic.message.lines().count() as u8;
5006 BlockProperties {
5007 position: buffer.anchor_after(entry.range.start),
5008 height: message_height,
5009 render: diagnostic_block_renderer(diagnostic, true),
5010 disposition: BlockDisposition::Below,
5011 }
5012 }),
5013 cx,
5014 )
5015 .into_iter()
5016 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5017 .collect();
5018
5019 Some(ActiveDiagnosticGroup {
5020 primary_range,
5021 primary_message,
5022 blocks,
5023 is_valid: true,
5024 })
5025 });
5026 }
5027
5028 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5029 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5030 self.display_map.update(cx, |display_map, cx| {
5031 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5032 });
5033 cx.notify();
5034 }
5035 }
5036
5037 fn build_columnar_selection(
5038 &mut self,
5039 display_map: &DisplaySnapshot,
5040 row: u32,
5041 columns: &Range<u32>,
5042 reversed: bool,
5043 ) -> Option<Selection<Point>> {
5044 let is_empty = columns.start == columns.end;
5045 let line_len = display_map.line_len(row);
5046 if columns.start < line_len || (is_empty && columns.start == line_len) {
5047 let start = DisplayPoint::new(row, columns.start);
5048 let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
5049 Some(Selection {
5050 id: post_inc(&mut self.next_selection_id),
5051 start: start.to_point(display_map),
5052 end: end.to_point(display_map),
5053 reversed,
5054 goal: SelectionGoal::ColumnRange {
5055 start: columns.start,
5056 end: columns.end,
5057 },
5058 })
5059 } else {
5060 None
5061 }
5062 }
5063
5064 pub fn local_selections_in_range(
5065 &self,
5066 range: Range<Anchor>,
5067 display_map: &DisplaySnapshot,
5068 ) -> Vec<Selection<Point>> {
5069 let buffer = &display_map.buffer_snapshot;
5070
5071 let start_ix = match self
5072 .selections
5073 .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer))
5074 {
5075 Ok(ix) | Err(ix) => ix,
5076 };
5077 let end_ix = match self
5078 .selections
5079 .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer))
5080 {
5081 Ok(ix) => ix + 1,
5082 Err(ix) => ix,
5083 };
5084
5085 fn point_selection(
5086 selection: &Selection<Anchor>,
5087 buffer: &MultiBufferSnapshot,
5088 ) -> Selection<Point> {
5089 let start = selection.start.to_point(&buffer);
5090 let end = selection.end.to_point(&buffer);
5091 Selection {
5092 id: selection.id,
5093 start,
5094 end,
5095 reversed: selection.reversed,
5096 goal: selection.goal,
5097 }
5098 }
5099
5100 self.selections[start_ix..end_ix]
5101 .iter()
5102 .chain(
5103 self.pending_selection
5104 .as_ref()
5105 .map(|pending| &pending.selection),
5106 )
5107 .map(|s| point_selection(s, &buffer))
5108 .collect()
5109 }
5110
5111 pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>>
5112 where
5113 D: 'a + TextDimension + Ord + Sub<D, Output = D>,
5114 {
5115 let buffer = self.buffer.read(cx).snapshot(cx);
5116 let mut selections = self
5117 .resolve_selections::<D, _>(self.selections.iter(), &buffer)
5118 .peekable();
5119
5120 let mut pending_selection = self.pending_selection::<D>(&buffer);
5121
5122 iter::from_fn(move || {
5123 if let Some(pending) = pending_selection.as_mut() {
5124 while let Some(next_selection) = selections.peek() {
5125 if pending.start <= next_selection.end && pending.end >= next_selection.start {
5126 let next_selection = selections.next().unwrap();
5127 if next_selection.start < pending.start {
5128 pending.start = next_selection.start;
5129 }
5130 if next_selection.end > pending.end {
5131 pending.end = next_selection.end;
5132 }
5133 } else if next_selection.end < pending.start {
5134 return selections.next();
5135 } else {
5136 break;
5137 }
5138 }
5139
5140 pending_selection.take()
5141 } else {
5142 selections.next()
5143 }
5144 })
5145 .collect()
5146 }
5147
5148 fn resolve_selections<'a, D, I>(
5149 &self,
5150 selections: I,
5151 snapshot: &MultiBufferSnapshot,
5152 ) -> impl 'a + Iterator<Item = Selection<D>>
5153 where
5154 D: TextDimension + Ord + Sub<D, Output = D>,
5155 I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
5156 {
5157 let (to_summarize, selections) = selections.into_iter().tee();
5158 let mut summaries = snapshot
5159 .summaries_for_anchors::<D, _>(to_summarize.flat_map(|s| [&s.start, &s.end]))
5160 .into_iter();
5161 selections.map(move |s| Selection {
5162 id: s.id,
5163 start: summaries.next().unwrap(),
5164 end: summaries.next().unwrap(),
5165 reversed: s.reversed,
5166 goal: s.goal,
5167 })
5168 }
5169
5170 fn pending_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5171 &self,
5172 snapshot: &MultiBufferSnapshot,
5173 ) -> Option<Selection<D>> {
5174 self.pending_selection
5175 .as_ref()
5176 .map(|pending| self.resolve_selection(&pending.selection, &snapshot))
5177 }
5178
5179 fn resolve_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5180 &self,
5181 selection: &Selection<Anchor>,
5182 buffer: &MultiBufferSnapshot,
5183 ) -> Selection<D> {
5184 Selection {
5185 id: selection.id,
5186 start: selection.start.summary::<D>(&buffer),
5187 end: selection.end.summary::<D>(&buffer),
5188 reversed: selection.reversed,
5189 goal: selection.goal,
5190 }
5191 }
5192
5193 fn selection_count<'a>(&self) -> usize {
5194 let mut count = self.selections.len();
5195 if self.pending_selection.is_some() {
5196 count += 1;
5197 }
5198 count
5199 }
5200
5201 pub fn oldest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5202 &self,
5203 cx: &AppContext,
5204 ) -> Selection<D> {
5205 let snapshot = self.buffer.read(cx).read(cx);
5206 self.selections
5207 .iter()
5208 .min_by_key(|s| s.id)
5209 .map(|selection| self.resolve_selection(selection, &snapshot))
5210 .or_else(|| self.pending_selection(&snapshot))
5211 .unwrap()
5212 }
5213
5214 pub fn newest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
5215 &self,
5216 cx: &AppContext,
5217 ) -> Selection<D> {
5218 self.resolve_selection(
5219 self.newest_anchor_selection(),
5220 &self.buffer.read(cx).read(cx),
5221 )
5222 }
5223
5224 pub fn newest_selection_with_snapshot<D: TextDimension + Ord + Sub<D, Output = D>>(
5225 &self,
5226 snapshot: &MultiBufferSnapshot,
5227 ) -> Selection<D> {
5228 self.resolve_selection(self.newest_anchor_selection(), snapshot)
5229 }
5230
5231 pub fn newest_anchor_selection(&self) -> &Selection<Anchor> {
5232 self.pending_selection
5233 .as_ref()
5234 .map(|s| &s.selection)
5235 .or_else(|| self.selections.iter().max_by_key(|s| s.id))
5236 .unwrap()
5237 }
5238
5239 pub fn update_selections<T>(
5240 &mut self,
5241 mut selections: Vec<Selection<T>>,
5242 autoscroll: Option<Autoscroll>,
5243 cx: &mut ViewContext<Self>,
5244 ) where
5245 T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
5246 {
5247 let buffer = self.buffer.read(cx).snapshot(cx);
5248 selections.sort_unstable_by_key(|s| s.start);
5249
5250 // Merge overlapping selections.
5251 let mut i = 1;
5252 while i < selections.len() {
5253 if selections[i - 1].end >= selections[i].start {
5254 let removed = selections.remove(i);
5255 if removed.start < selections[i - 1].start {
5256 selections[i - 1].start = removed.start;
5257 }
5258 if removed.end > selections[i - 1].end {
5259 selections[i - 1].end = removed.end;
5260 }
5261 } else {
5262 i += 1;
5263 }
5264 }
5265
5266 if let Some(autoscroll) = autoscroll {
5267 self.request_autoscroll(autoscroll, cx);
5268 }
5269
5270 self.set_selections(
5271 Arc::from_iter(selections.into_iter().map(|selection| {
5272 let end_bias = if selection.end > selection.start {
5273 Bias::Left
5274 } else {
5275 Bias::Right
5276 };
5277 Selection {
5278 id: selection.id,
5279 start: buffer.anchor_after(selection.start),
5280 end: buffer.anchor_at(selection.end, end_bias),
5281 reversed: selection.reversed,
5282 goal: selection.goal,
5283 }
5284 })),
5285 None,
5286 true,
5287 cx,
5288 );
5289 }
5290
5291 pub fn set_selections_from_remote(
5292 &mut self,
5293 mut selections: Vec<Selection<Anchor>>,
5294 cx: &mut ViewContext<Self>,
5295 ) {
5296 let buffer = self.buffer.read(cx);
5297 let buffer = buffer.read(cx);
5298 selections.sort_by(|a, b| {
5299 a.start
5300 .cmp(&b.start, &*buffer)
5301 .then_with(|| b.end.cmp(&a.end, &*buffer))
5302 });
5303
5304 // Merge overlapping selections
5305 let mut i = 1;
5306 while i < selections.len() {
5307 if selections[i - 1]
5308 .end
5309 .cmp(&selections[i].start, &*buffer)
5310 .is_ge()
5311 {
5312 let removed = selections.remove(i);
5313 if removed
5314 .start
5315 .cmp(&selections[i - 1].start, &*buffer)
5316 .is_lt()
5317 {
5318 selections[i - 1].start = removed.start;
5319 }
5320 if removed.end.cmp(&selections[i - 1].end, &*buffer).is_gt() {
5321 selections[i - 1].end = removed.end;
5322 }
5323 } else {
5324 i += 1;
5325 }
5326 }
5327
5328 drop(buffer);
5329 self.set_selections(selections.into(), None, false, cx);
5330 }
5331
5332 /// Compute new ranges for any selections that were located in excerpts that have
5333 /// since been removed.
5334 ///
5335 /// Returns a `HashMap` indicating which selections whose former head position
5336 /// was no longer present. The keys of the map are selection ids. The values are
5337 /// the id of the new excerpt where the head of the selection has been moved.
5338 pub fn refresh_selections(&mut self, cx: &mut ViewContext<Self>) -> HashMap<usize, ExcerptId> {
5339 let snapshot = self.buffer.read(cx).read(cx);
5340 let mut selections_with_lost_position = HashMap::default();
5341
5342 let mut pending_selection = self.pending_selection.take();
5343 if let Some(pending) = pending_selection.as_mut() {
5344 let anchors =
5345 snapshot.refresh_anchors([&pending.selection.start, &pending.selection.end]);
5346 let (_, start, kept_start) = anchors[0].clone();
5347 let (_, end, kept_end) = anchors[1].clone();
5348 let kept_head = if pending.selection.reversed {
5349 kept_start
5350 } else {
5351 kept_end
5352 };
5353 if !kept_head {
5354 selections_with_lost_position.insert(
5355 pending.selection.id,
5356 pending.selection.head().excerpt_id.clone(),
5357 );
5358 }
5359
5360 pending.selection.start = start;
5361 pending.selection.end = end;
5362 }
5363
5364 let anchors_with_status = snapshot.refresh_anchors(
5365 self.selections
5366 .iter()
5367 .flat_map(|selection| [&selection.start, &selection.end]),
5368 );
5369 self.selections = anchors_with_status
5370 .chunks(2)
5371 .map(|selection_anchors| {
5372 let (anchor_ix, start, kept_start) = selection_anchors[0].clone();
5373 let (_, end, kept_end) = selection_anchors[1].clone();
5374 let selection = &self.selections[anchor_ix / 2];
5375 let kept_head = if selection.reversed {
5376 kept_start
5377 } else {
5378 kept_end
5379 };
5380 if !kept_head {
5381 selections_with_lost_position
5382 .insert(selection.id, selection.head().excerpt_id.clone());
5383 }
5384
5385 Selection {
5386 id: selection.id,
5387 start,
5388 end,
5389 reversed: selection.reversed,
5390 goal: selection.goal,
5391 }
5392 })
5393 .collect();
5394 drop(snapshot);
5395
5396 let new_selections = self.local_selections::<usize>(cx);
5397 if !new_selections.is_empty() {
5398 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
5399 }
5400 self.pending_selection = pending_selection;
5401
5402 selections_with_lost_position
5403 }
5404
5405 fn set_selections(
5406 &mut self,
5407 selections: Arc<[Selection<Anchor>]>,
5408 pending_selection: Option<PendingSelection>,
5409 local: bool,
5410 cx: &mut ViewContext<Self>,
5411 ) {
5412 assert!(
5413 !selections.is_empty() || pending_selection.is_some(),
5414 "must have at least one selection"
5415 );
5416
5417 let old_cursor_position = self.newest_anchor_selection().head();
5418
5419 self.push_to_selection_history();
5420 self.selections = selections;
5421 self.pending_selection = pending_selection;
5422 if self.focused && self.leader_replica_id.is_none() {
5423 self.buffer.update(cx, |buffer, cx| {
5424 buffer.set_active_selections(&self.selections, cx)
5425 });
5426 }
5427
5428 let display_map = self
5429 .display_map
5430 .update(cx, |display_map, cx| display_map.snapshot(cx));
5431 let buffer = &display_map.buffer_snapshot;
5432 self.add_selections_state = None;
5433 self.select_next_state = None;
5434 self.select_larger_syntax_node_stack.clear();
5435 self.autoclose_stack.invalidate(&self.selections, &buffer);
5436 self.snippet_stack.invalidate(&self.selections, &buffer);
5437 self.take_rename(false, cx);
5438
5439 let new_cursor_position = self.newest_anchor_selection().head();
5440
5441 self.push_to_nav_history(
5442 old_cursor_position.clone(),
5443 Some(new_cursor_position.to_point(&buffer)),
5444 cx,
5445 );
5446
5447 if local {
5448 let completion_menu = match self.context_menu.as_mut() {
5449 Some(ContextMenu::Completions(menu)) => Some(menu),
5450 _ => {
5451 self.context_menu.take();
5452 None
5453 }
5454 };
5455
5456 if let Some(completion_menu) = completion_menu {
5457 let cursor_position = new_cursor_position.to_offset(&buffer);
5458 let (word_range, kind) =
5459 buffer.surrounding_word(completion_menu.initial_position.clone());
5460 if kind == Some(CharKind::Word)
5461 && word_range.to_inclusive().contains(&cursor_position)
5462 {
5463 let query = Self::completion_query(&buffer, cursor_position);
5464 cx.background()
5465 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
5466 self.show_completions(&ShowCompletions, cx);
5467 } else {
5468 self.hide_context_menu(cx);
5469 }
5470 }
5471
5472 if old_cursor_position.to_display_point(&display_map).row()
5473 != new_cursor_position.to_display_point(&display_map).row()
5474 {
5475 self.available_code_actions.take();
5476 }
5477 self.refresh_code_actions(cx);
5478 self.refresh_document_highlights(cx);
5479 }
5480
5481 self.pause_cursor_blinking(cx);
5482 cx.emit(Event::SelectionsChanged { local });
5483 }
5484
5485 fn push_to_selection_history(&mut self) {
5486 self.selection_history.push(SelectionHistoryEntry {
5487 selections: self.selections.clone(),
5488 select_next_state: self.select_next_state.clone(),
5489 add_selections_state: self.add_selections_state.clone(),
5490 });
5491 }
5492
5493 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5494 self.autoscroll_request = Some((autoscroll, true));
5495 cx.notify();
5496 }
5497
5498 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5499 self.autoscroll_request = Some((autoscroll, false));
5500 cx.notify();
5501 }
5502
5503 pub fn transact(
5504 &mut self,
5505 cx: &mut ViewContext<Self>,
5506 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5507 ) {
5508 self.start_transaction_at(Instant::now(), cx);
5509 update(self, cx);
5510 self.end_transaction_at(Instant::now(), cx);
5511 }
5512
5513 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5514 self.end_selection(cx);
5515 if let Some(tx_id) = self
5516 .buffer
5517 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5518 {
5519 self.selection_history
5520 .insert_transaction(tx_id, self.selections.clone());
5521 }
5522 }
5523
5524 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5525 if let Some(tx_id) = self
5526 .buffer
5527 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5528 {
5529 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5530 *end_selections = Some(self.selections.clone());
5531 } else {
5532 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5533 }
5534
5535 cx.emit(Event::Edited);
5536 }
5537 }
5538
5539 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5540 log::info!("Editor::page_up");
5541 }
5542
5543 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5544 log::info!("Editor::page_down");
5545 }
5546
5547 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5548 let mut fold_ranges = Vec::new();
5549
5550 let selections = self.local_selections::<Point>(cx);
5551 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5552 for selection in selections {
5553 let range = selection.display_range(&display_map).sorted();
5554 let buffer_start_row = range.start.to_point(&display_map).row;
5555
5556 for row in (0..=range.end.row()).rev() {
5557 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5558 let fold_range = self.foldable_range_for_line(&display_map, row);
5559 if fold_range.end.row >= buffer_start_row {
5560 fold_ranges.push(fold_range);
5561 if row <= range.start.row() {
5562 break;
5563 }
5564 }
5565 }
5566 }
5567 }
5568
5569 self.fold_ranges(fold_ranges, cx);
5570 }
5571
5572 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5573 let selections = self.local_selections::<Point>(cx);
5574 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5575 let buffer = &display_map.buffer_snapshot;
5576 let ranges = selections
5577 .iter()
5578 .map(|s| {
5579 let range = s.display_range(&display_map).sorted();
5580 let mut start = range.start.to_point(&display_map);
5581 let mut end = range.end.to_point(&display_map);
5582 start.column = 0;
5583 end.column = buffer.line_len(end.row);
5584 start..end
5585 })
5586 .collect::<Vec<_>>();
5587 self.unfold_ranges(ranges, true, cx);
5588 }
5589
5590 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5591 let max_point = display_map.max_point();
5592 if display_row >= max_point.row() {
5593 false
5594 } else {
5595 let (start_indent, is_blank) = display_map.line_indent(display_row);
5596 if is_blank {
5597 false
5598 } else {
5599 for display_row in display_row + 1..=max_point.row() {
5600 let (indent, is_blank) = display_map.line_indent(display_row);
5601 if !is_blank {
5602 return indent > start_indent;
5603 }
5604 }
5605 false
5606 }
5607 }
5608 }
5609
5610 fn foldable_range_for_line(
5611 &self,
5612 display_map: &DisplaySnapshot,
5613 start_row: u32,
5614 ) -> Range<Point> {
5615 let max_point = display_map.max_point();
5616
5617 let (start_indent, _) = display_map.line_indent(start_row);
5618 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5619 let mut end = None;
5620 for row in start_row + 1..=max_point.row() {
5621 let (indent, is_blank) = display_map.line_indent(row);
5622 if !is_blank && indent <= start_indent {
5623 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5624 break;
5625 }
5626 }
5627
5628 let end = end.unwrap_or(max_point);
5629 return start.to_point(display_map)..end.to_point(display_map);
5630 }
5631
5632 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5633 let selections = self.local_selections::<Point>(cx);
5634 let ranges = selections.into_iter().map(|s| s.start..s.end);
5635 self.fold_ranges(ranges, cx);
5636 }
5637
5638 pub fn fold_ranges<T: ToOffset>(
5639 &mut self,
5640 ranges: impl IntoIterator<Item = Range<T>>,
5641 cx: &mut ViewContext<Self>,
5642 ) {
5643 let mut ranges = ranges.into_iter().peekable();
5644 if ranges.peek().is_some() {
5645 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5646 self.request_autoscroll(Autoscroll::Fit, cx);
5647 cx.notify();
5648 }
5649 }
5650
5651 pub fn unfold_ranges<T: ToOffset>(
5652 &mut self,
5653 ranges: impl IntoIterator<Item = Range<T>>,
5654 inclusive: bool,
5655 cx: &mut ViewContext<Self>,
5656 ) {
5657 let mut ranges = ranges.into_iter().peekable();
5658 if ranges.peek().is_some() {
5659 self.display_map
5660 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5661 self.request_autoscroll(Autoscroll::Fit, cx);
5662 cx.notify();
5663 }
5664 }
5665
5666 pub fn insert_blocks(
5667 &mut self,
5668 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5669 cx: &mut ViewContext<Self>,
5670 ) -> Vec<BlockId> {
5671 let blocks = self
5672 .display_map
5673 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5674 self.request_autoscroll(Autoscroll::Fit, cx);
5675 blocks
5676 }
5677
5678 pub fn replace_blocks(
5679 &mut self,
5680 blocks: HashMap<BlockId, RenderBlock>,
5681 cx: &mut ViewContext<Self>,
5682 ) {
5683 self.display_map
5684 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5685 self.request_autoscroll(Autoscroll::Fit, cx);
5686 }
5687
5688 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5689 self.display_map.update(cx, |display_map, cx| {
5690 display_map.remove_blocks(block_ids, cx)
5691 });
5692 }
5693
5694 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5695 self.display_map
5696 .update(cx, |map, cx| map.snapshot(cx))
5697 .longest_row()
5698 }
5699
5700 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5701 self.display_map
5702 .update(cx, |map, cx| map.snapshot(cx))
5703 .max_point()
5704 }
5705
5706 pub fn text(&self, cx: &AppContext) -> String {
5707 self.buffer.read(cx).read(cx).text()
5708 }
5709
5710 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5711 self.transact(cx, |this, cx| {
5712 this.buffer
5713 .read(cx)
5714 .as_singleton()
5715 .expect("you can only call set_text on editors for singleton buffers")
5716 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5717 });
5718 }
5719
5720 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5721 self.display_map
5722 .update(cx, |map, cx| map.snapshot(cx))
5723 .text()
5724 }
5725
5726 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5727 let language_name = self
5728 .buffer
5729 .read(cx)
5730 .as_singleton()
5731 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5732 .map(|l| l.name());
5733
5734 let settings = cx.global::<Settings>();
5735 let mode = self
5736 .soft_wrap_mode_override
5737 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5738 match mode {
5739 settings::SoftWrap::None => SoftWrap::None,
5740 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5741 settings::SoftWrap::PreferredLineLength => {
5742 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5743 }
5744 }
5745 }
5746
5747 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5748 self.soft_wrap_mode_override = Some(mode);
5749 cx.notify();
5750 }
5751
5752 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5753 self.display_map
5754 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5755 }
5756
5757 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5758 self.highlighted_rows = rows;
5759 }
5760
5761 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5762 self.highlighted_rows.clone()
5763 }
5764
5765 pub fn highlight_background<T: 'static>(
5766 &mut self,
5767 ranges: Vec<Range<Anchor>>,
5768 color_fetcher: fn(&Theme) -> Color,
5769 cx: &mut ViewContext<Self>,
5770 ) {
5771 self.background_highlights
5772 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5773 cx.notify();
5774 }
5775
5776 pub fn clear_background_highlights<T: 'static>(
5777 &mut self,
5778 cx: &mut ViewContext<Self>,
5779 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5780 cx.notify();
5781 self.background_highlights.remove(&TypeId::of::<T>())
5782 }
5783
5784 #[cfg(feature = "test-support")]
5785 pub fn all_background_highlights(
5786 &mut self,
5787 cx: &mut ViewContext<Self>,
5788 ) -> Vec<(Range<DisplayPoint>, Color)> {
5789 let snapshot = self.snapshot(cx);
5790 let buffer = &snapshot.buffer_snapshot;
5791 let start = buffer.anchor_before(0);
5792 let end = buffer.anchor_after(buffer.len());
5793 let theme = cx.global::<Settings>().theme.as_ref();
5794 self.background_highlights_in_range(start..end, &snapshot, theme)
5795 }
5796
5797 fn document_highlights_for_position<'a>(
5798 &'a self,
5799 position: Anchor,
5800 buffer: &'a MultiBufferSnapshot,
5801 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5802 let read_highlights = self
5803 .background_highlights
5804 .get(&TypeId::of::<DocumentHighlightRead>())
5805 .map(|h| &h.1);
5806 let write_highlights = self
5807 .background_highlights
5808 .get(&TypeId::of::<DocumentHighlightRead>())
5809 .map(|h| &h.1);
5810 let left_position = position.bias_left(buffer);
5811 let right_position = position.bias_right(buffer);
5812 read_highlights
5813 .into_iter()
5814 .chain(write_highlights)
5815 .flat_map(move |ranges| {
5816 let start_ix = match ranges.binary_search_by(|probe| {
5817 let cmp = probe.end.cmp(&left_position, &buffer);
5818 if cmp.is_ge() {
5819 Ordering::Greater
5820 } else {
5821 Ordering::Less
5822 }
5823 }) {
5824 Ok(i) | Err(i) => i,
5825 };
5826
5827 let right_position = right_position.clone();
5828 ranges[start_ix..]
5829 .iter()
5830 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5831 })
5832 }
5833
5834 pub fn background_highlights_in_range(
5835 &self,
5836 search_range: Range<Anchor>,
5837 display_snapshot: &DisplaySnapshot,
5838 theme: &Theme,
5839 ) -> Vec<(Range<DisplayPoint>, Color)> {
5840 let mut results = Vec::new();
5841 let buffer = &display_snapshot.buffer_snapshot;
5842 for (color_fetcher, ranges) in self.background_highlights.values() {
5843 let color = color_fetcher(theme);
5844 let start_ix = match ranges.binary_search_by(|probe| {
5845 let cmp = probe.end.cmp(&search_range.start, &buffer);
5846 if cmp.is_gt() {
5847 Ordering::Greater
5848 } else {
5849 Ordering::Less
5850 }
5851 }) {
5852 Ok(i) | Err(i) => i,
5853 };
5854 for range in &ranges[start_ix..] {
5855 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5856 break;
5857 }
5858 let start = range
5859 .start
5860 .to_point(buffer)
5861 .to_display_point(display_snapshot);
5862 let end = range
5863 .end
5864 .to_point(buffer)
5865 .to_display_point(display_snapshot);
5866 results.push((start..end, color))
5867 }
5868 }
5869 results
5870 }
5871
5872 pub fn highlight_text<T: 'static>(
5873 &mut self,
5874 ranges: Vec<Range<Anchor>>,
5875 style: HighlightStyle,
5876 cx: &mut ViewContext<Self>,
5877 ) {
5878 self.display_map.update(cx, |map, _| {
5879 map.highlight_text(TypeId::of::<T>(), ranges, style)
5880 });
5881 cx.notify();
5882 }
5883
5884 pub fn clear_text_highlights<T: 'static>(
5885 &mut self,
5886 cx: &mut ViewContext<Self>,
5887 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5888 cx.notify();
5889 self.display_map
5890 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5891 }
5892
5893 fn next_blink_epoch(&mut self) -> usize {
5894 self.blink_epoch += 1;
5895 self.blink_epoch
5896 }
5897
5898 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5899 if !self.focused {
5900 return;
5901 }
5902
5903 self.show_local_cursors = true;
5904 cx.notify();
5905
5906 let epoch = self.next_blink_epoch();
5907 cx.spawn(|this, mut cx| {
5908 let this = this.downgrade();
5909 async move {
5910 Timer::after(CURSOR_BLINK_INTERVAL).await;
5911 if let Some(this) = this.upgrade(&cx) {
5912 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5913 }
5914 }
5915 })
5916 .detach();
5917 }
5918
5919 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5920 if epoch == self.blink_epoch {
5921 self.blinking_paused = false;
5922 self.blink_cursors(epoch, cx);
5923 }
5924 }
5925
5926 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5927 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5928 self.show_local_cursors = !self.show_local_cursors;
5929 cx.notify();
5930
5931 let epoch = self.next_blink_epoch();
5932 cx.spawn(|this, mut cx| {
5933 let this = this.downgrade();
5934 async move {
5935 Timer::after(CURSOR_BLINK_INTERVAL).await;
5936 if let Some(this) = this.upgrade(&cx) {
5937 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5938 }
5939 }
5940 })
5941 .detach();
5942 }
5943 }
5944
5945 pub fn show_local_cursors(&self) -> bool {
5946 self.show_local_cursors && self.focused
5947 }
5948
5949 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5950 cx.notify();
5951 }
5952
5953 fn on_buffer_event(
5954 &mut self,
5955 _: ModelHandle<MultiBuffer>,
5956 event: &language::Event,
5957 cx: &mut ViewContext<Self>,
5958 ) {
5959 match event {
5960 language::Event::Edited => {
5961 self.refresh_active_diagnostics(cx);
5962 self.refresh_code_actions(cx);
5963 cx.emit(Event::BufferEdited);
5964 }
5965 language::Event::Reparsed => cx.emit(Event::Reparsed),
5966 language::Event::Dirtied => cx.emit(Event::Dirtied),
5967 language::Event::Saved => cx.emit(Event::Saved),
5968 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5969 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5970 language::Event::Closed => cx.emit(Event::Closed),
5971 language::Event::DiagnosticsUpdated => {
5972 self.refresh_active_diagnostics(cx);
5973 }
5974 _ => {}
5975 }
5976 }
5977
5978 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5979 cx.notify();
5980 }
5981
5982 pub fn set_searchable(&mut self, searchable: bool) {
5983 self.searchable = searchable;
5984 }
5985
5986 pub fn searchable(&self) -> bool {
5987 self.searchable
5988 }
5989
5990 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5991 let active_item = workspace.active_item(cx);
5992 let editor_handle = if let Some(editor) = active_item
5993 .as_ref()
5994 .and_then(|item| item.act_as::<Self>(cx))
5995 {
5996 editor
5997 } else {
5998 cx.propagate_action();
5999 return;
6000 };
6001
6002 let editor = editor_handle.read(cx);
6003 let buffer = editor.buffer.read(cx);
6004 if buffer.is_singleton() {
6005 cx.propagate_action();
6006 return;
6007 }
6008
6009 let mut new_selections_by_buffer = HashMap::default();
6010 for selection in editor.local_selections::<usize>(cx) {
6011 for (buffer, mut range) in
6012 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6013 {
6014 if selection.reversed {
6015 mem::swap(&mut range.start, &mut range.end);
6016 }
6017 new_selections_by_buffer
6018 .entry(buffer)
6019 .or_insert(Vec::new())
6020 .push(range)
6021 }
6022 }
6023
6024 editor_handle.update(cx, |editor, cx| {
6025 editor.push_to_nav_history(editor.newest_anchor_selection().head(), None, cx);
6026 });
6027 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
6028 nav_history.borrow_mut().disable();
6029
6030 // We defer the pane interaction because we ourselves are a workspace item
6031 // and activating a new item causes the pane to call a method on us reentrantly,
6032 // which panics if we're on the stack.
6033 cx.defer(move |workspace, cx| {
6034 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6035 let editor = workspace.open_project_item::<Self>(buffer, cx);
6036 editor.update(cx, |editor, cx| {
6037 editor.select_ranges(ranges, Some(Autoscroll::Newest), cx);
6038 });
6039 }
6040
6041 nav_history.borrow_mut().enable();
6042 });
6043 }
6044}
6045
6046impl EditorSnapshot {
6047 pub fn is_focused(&self) -> bool {
6048 self.is_focused
6049 }
6050
6051 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6052 self.placeholder_text.as_ref()
6053 }
6054
6055 pub fn scroll_position(&self) -> Vector2F {
6056 compute_scroll_position(
6057 &self.display_snapshot,
6058 self.scroll_position,
6059 &self.scroll_top_anchor,
6060 )
6061 }
6062}
6063
6064impl Deref for EditorSnapshot {
6065 type Target = DisplaySnapshot;
6066
6067 fn deref(&self) -> &Self::Target {
6068 &self.display_snapshot
6069 }
6070}
6071
6072fn compute_scroll_position(
6073 snapshot: &DisplaySnapshot,
6074 mut scroll_position: Vector2F,
6075 scroll_top_anchor: &Anchor,
6076) -> Vector2F {
6077 if *scroll_top_anchor != Anchor::min() {
6078 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
6079 scroll_position.set_y(scroll_top + scroll_position.y());
6080 } else {
6081 scroll_position.set_y(0.);
6082 }
6083 scroll_position
6084}
6085
6086#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6087pub enum Event {
6088 Activate,
6089 BufferEdited,
6090 Edited,
6091 Reparsed,
6092 Blurred,
6093 Dirtied,
6094 Saved,
6095 TitleChanged,
6096 SelectionsChanged { local: bool },
6097 ScrollPositionChanged { local: bool },
6098 Closed,
6099}
6100
6101pub struct EditorFocused(pub ViewHandle<Editor>);
6102pub struct EditorBlurred(pub ViewHandle<Editor>);
6103pub struct EditorReleased(pub WeakViewHandle<Editor>);
6104
6105impl Entity for Editor {
6106 type Event = Event;
6107
6108 fn release(&mut self, cx: &mut MutableAppContext) {
6109 cx.emit_global(EditorReleased(self.handle.clone()));
6110 }
6111}
6112
6113impl View for Editor {
6114 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6115 let style = self.style(cx);
6116 self.display_map.update(cx, |map, cx| {
6117 map.set_font(style.text.font_id, style.text.font_size, cx)
6118 });
6119 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
6120 }
6121
6122 fn ui_name() -> &'static str {
6123 "Editor"
6124 }
6125
6126 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
6127 let focused_event = EditorFocused(cx.handle());
6128 cx.emit_global(focused_event);
6129 if let Some(rename) = self.pending_rename.as_ref() {
6130 cx.focus(&rename.editor);
6131 } else {
6132 self.focused = true;
6133 self.blink_cursors(self.blink_epoch, cx);
6134 self.buffer.update(cx, |buffer, cx| {
6135 buffer.finalize_last_transaction(cx);
6136 if self.leader_replica_id.is_none() {
6137 buffer.set_active_selections(&self.selections, cx);
6138 }
6139 });
6140 }
6141 }
6142
6143 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
6144 let blurred_event = EditorBlurred(cx.handle());
6145 cx.emit_global(blurred_event);
6146 self.focused = false;
6147 self.buffer
6148 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6149 self.hide_context_menu(cx);
6150 cx.emit(Event::Blurred);
6151 cx.notify();
6152 }
6153
6154 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
6155 let mut context = Self::default_keymap_context();
6156 let mode = match self.mode {
6157 EditorMode::SingleLine => "single_line",
6158 EditorMode::AutoHeight { .. } => "auto_height",
6159 EditorMode::Full => "full",
6160 };
6161 context.map.insert("mode".into(), mode.into());
6162 if self.pending_rename.is_some() {
6163 context.set.insert("renaming".into());
6164 }
6165 match self.context_menu.as_ref() {
6166 Some(ContextMenu::Completions(_)) => {
6167 context.set.insert("showing_completions".into());
6168 }
6169 Some(ContextMenu::CodeActions(_)) => {
6170 context.set.insert("showing_code_actions".into());
6171 }
6172 None => {}
6173 }
6174
6175 for layer in self.keymap_context_layers.values() {
6176 context.extend(layer);
6177 }
6178
6179 context
6180 }
6181}
6182
6183fn build_style(
6184 settings: &Settings,
6185 get_field_editor_theme: Option<GetFieldEditorTheme>,
6186 override_text_style: Option<&OverrideTextStyle>,
6187 cx: &AppContext,
6188) -> EditorStyle {
6189 let font_cache = cx.font_cache();
6190
6191 let mut theme = settings.theme.editor.clone();
6192 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6193 let field_editor_theme = get_field_editor_theme(&settings.theme);
6194 theme.text_color = field_editor_theme.text.color;
6195 theme.selection = field_editor_theme.selection;
6196 theme.background = field_editor_theme
6197 .container
6198 .background_color
6199 .unwrap_or_default();
6200 EditorStyle {
6201 text: field_editor_theme.text,
6202 placeholder_text: field_editor_theme.placeholder_text,
6203 theme,
6204 }
6205 } else {
6206 let font_family_id = settings.buffer_font_family;
6207 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6208 let font_properties = Default::default();
6209 let font_id = font_cache
6210 .select_font(font_family_id, &font_properties)
6211 .unwrap();
6212 let font_size = settings.buffer_font_size;
6213 EditorStyle {
6214 text: TextStyle {
6215 color: settings.theme.editor.text_color,
6216 font_family_name,
6217 font_family_id,
6218 font_id,
6219 font_size,
6220 font_properties,
6221 underline: Default::default(),
6222 },
6223 placeholder_text: None,
6224 theme,
6225 }
6226 };
6227
6228 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6229 if let Some(highlighted) = style
6230 .text
6231 .clone()
6232 .highlight(highlight_style, font_cache)
6233 .log_err()
6234 {
6235 style.text = highlighted;
6236 }
6237 }
6238
6239 style
6240}
6241
6242trait SelectionExt {
6243 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6244 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6245 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6246 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6247 -> Range<u32>;
6248}
6249
6250impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6251 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6252 let start = self.start.to_point(buffer);
6253 let end = self.end.to_point(buffer);
6254 if self.reversed {
6255 end..start
6256 } else {
6257 start..end
6258 }
6259 }
6260
6261 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6262 let start = self.start.to_offset(buffer);
6263 let end = self.end.to_offset(buffer);
6264 if self.reversed {
6265 end..start
6266 } else {
6267 start..end
6268 }
6269 }
6270
6271 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6272 let start = self
6273 .start
6274 .to_point(&map.buffer_snapshot)
6275 .to_display_point(map);
6276 let end = self
6277 .end
6278 .to_point(&map.buffer_snapshot)
6279 .to_display_point(map);
6280 if self.reversed {
6281 end..start
6282 } else {
6283 start..end
6284 }
6285 }
6286
6287 fn spanned_rows(
6288 &self,
6289 include_end_if_at_line_start: bool,
6290 map: &DisplaySnapshot,
6291 ) -> Range<u32> {
6292 let start = self.start.to_point(&map.buffer_snapshot);
6293 let mut end = self.end.to_point(&map.buffer_snapshot);
6294 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6295 end.row -= 1;
6296 }
6297
6298 let buffer_start = map.prev_line_boundary(start).0;
6299 let buffer_end = map.next_line_boundary(end).0;
6300 buffer_start.row..buffer_end.row + 1
6301 }
6302}
6303
6304impl<T: InvalidationRegion> InvalidationStack<T> {
6305 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6306 where
6307 S: Clone + ToOffset,
6308 {
6309 while let Some(region) = self.last() {
6310 let all_selections_inside_invalidation_ranges =
6311 if selections.len() == region.ranges().len() {
6312 selections
6313 .iter()
6314 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
6315 .all(|(selection, invalidation_range)| {
6316 let head = selection.head().to_offset(&buffer);
6317 invalidation_range.start <= head && invalidation_range.end >= head
6318 })
6319 } else {
6320 false
6321 };
6322
6323 if all_selections_inside_invalidation_ranges {
6324 break;
6325 } else {
6326 self.pop();
6327 }
6328 }
6329 }
6330}
6331
6332impl<T> Default for InvalidationStack<T> {
6333 fn default() -> Self {
6334 Self(Default::default())
6335 }
6336}
6337
6338impl<T> Deref for InvalidationStack<T> {
6339 type Target = Vec<T>;
6340
6341 fn deref(&self) -> &Self::Target {
6342 &self.0
6343 }
6344}
6345
6346impl<T> DerefMut for InvalidationStack<T> {
6347 fn deref_mut(&mut self) -> &mut Self::Target {
6348 &mut self.0
6349 }
6350}
6351
6352impl InvalidationRegion for BracketPairState {
6353 fn ranges(&self) -> &[Range<Anchor>] {
6354 &self.ranges
6355 }
6356}
6357
6358impl InvalidationRegion for SnippetState {
6359 fn ranges(&self) -> &[Range<Anchor>] {
6360 &self.ranges[self.active_index]
6361 }
6362}
6363
6364impl Deref for EditorStyle {
6365 type Target = theme::Editor;
6366
6367 fn deref(&self) -> &Self::Target {
6368 &self.theme
6369 }
6370}
6371
6372pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6373 let mut highlighted_lines = Vec::new();
6374 for line in diagnostic.message.lines() {
6375 highlighted_lines.push(highlight_diagnostic_message(line));
6376 }
6377
6378 Arc::new(move |cx: &BlockContext| {
6379 let settings = cx.global::<Settings>();
6380 let theme = &settings.theme.editor;
6381 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6382 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6383 Flex::column()
6384 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6385 Label::new(
6386 line.clone(),
6387 style.message.clone().with_font_size(font_size),
6388 )
6389 .with_highlights(highlights.clone())
6390 .contained()
6391 .with_margin_left(cx.anchor_x)
6392 .boxed()
6393 }))
6394 .aligned()
6395 .left()
6396 .boxed()
6397 })
6398}
6399
6400pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6401 let mut message_without_backticks = String::new();
6402 let mut prev_offset = 0;
6403 let mut inside_block = false;
6404 let mut highlights = Vec::new();
6405 for (match_ix, (offset, _)) in message
6406 .match_indices('`')
6407 .chain([(message.len(), "")])
6408 .enumerate()
6409 {
6410 message_without_backticks.push_str(&message[prev_offset..offset]);
6411 if inside_block {
6412 highlights.extend(prev_offset - match_ix..offset - match_ix);
6413 }
6414
6415 inside_block = !inside_block;
6416 prev_offset = offset + 1;
6417 }
6418
6419 (message_without_backticks, highlights)
6420}
6421
6422pub fn diagnostic_style(
6423 severity: DiagnosticSeverity,
6424 valid: bool,
6425 theme: &theme::Editor,
6426) -> DiagnosticStyle {
6427 match (severity, valid) {
6428 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6429 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6430 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6431 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6432 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6433 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6434 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6435 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6436 _ => theme.invalid_hint_diagnostic.clone(),
6437 }
6438}
6439
6440pub fn combine_syntax_and_fuzzy_match_highlights(
6441 text: &str,
6442 default_style: HighlightStyle,
6443 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6444 match_indices: &[usize],
6445) -> Vec<(Range<usize>, HighlightStyle)> {
6446 let mut result = Vec::new();
6447 let mut match_indices = match_indices.iter().copied().peekable();
6448
6449 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6450 {
6451 syntax_highlight.weight = None;
6452
6453 // Add highlights for any fuzzy match characters before the next
6454 // syntax highlight range.
6455 while let Some(&match_index) = match_indices.peek() {
6456 if match_index >= range.start {
6457 break;
6458 }
6459 match_indices.next();
6460 let end_index = char_ix_after(match_index, text);
6461 let mut match_style = default_style;
6462 match_style.weight = Some(fonts::Weight::BOLD);
6463 result.push((match_index..end_index, match_style));
6464 }
6465
6466 if range.start == usize::MAX {
6467 break;
6468 }
6469
6470 // Add highlights for any fuzzy match characters within the
6471 // syntax highlight range.
6472 let mut offset = range.start;
6473 while let Some(&match_index) = match_indices.peek() {
6474 if match_index >= range.end {
6475 break;
6476 }
6477
6478 match_indices.next();
6479 if match_index > offset {
6480 result.push((offset..match_index, syntax_highlight));
6481 }
6482
6483 let mut end_index = char_ix_after(match_index, text);
6484 while let Some(&next_match_index) = match_indices.peek() {
6485 if next_match_index == end_index && next_match_index < range.end {
6486 end_index = char_ix_after(next_match_index, text);
6487 match_indices.next();
6488 } else {
6489 break;
6490 }
6491 }
6492
6493 let mut match_style = syntax_highlight;
6494 match_style.weight = Some(fonts::Weight::BOLD);
6495 result.push((match_index..end_index, match_style));
6496 offset = end_index;
6497 }
6498
6499 if offset < range.end {
6500 result.push((offset..range.end, syntax_highlight));
6501 }
6502 }
6503
6504 fn char_ix_after(ix: usize, text: &str) -> usize {
6505 ix + text[ix..].chars().next().unwrap().len_utf8()
6506 }
6507
6508 result
6509}
6510
6511pub fn styled_runs_for_code_label<'a>(
6512 label: &'a CodeLabel,
6513 syntax_theme: &'a theme::SyntaxTheme,
6514) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6515 let fade_out = HighlightStyle {
6516 fade_out: Some(0.35),
6517 ..Default::default()
6518 };
6519
6520 let mut prev_end = label.filter_range.end;
6521 label
6522 .runs
6523 .iter()
6524 .enumerate()
6525 .flat_map(move |(ix, (range, highlight_id))| {
6526 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6527 style
6528 } else {
6529 return Default::default();
6530 };
6531 let mut muted_style = style.clone();
6532 muted_style.highlight(fade_out);
6533
6534 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6535 if range.start >= label.filter_range.end {
6536 if range.start > prev_end {
6537 runs.push((prev_end..range.start, fade_out));
6538 }
6539 runs.push((range.clone(), muted_style));
6540 } else if range.end <= label.filter_range.end {
6541 runs.push((range.clone(), style));
6542 } else {
6543 runs.push((range.start..label.filter_range.end, style));
6544 runs.push((label.filter_range.end..range.end, muted_style));
6545 }
6546 prev_end = cmp::max(prev_end, range.end);
6547
6548 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6549 runs.push((prev_end..label.text.len(), fade_out));
6550 }
6551
6552 runs
6553 })
6554}
6555
6556#[cfg(test)]
6557mod tests {
6558 use crate::test::{assert_text_with_selections, select_ranges};
6559
6560 use super::*;
6561 use gpui::{
6562 geometry::rect::RectF,
6563 platform::{WindowBounds, WindowOptions},
6564 };
6565 use indoc::indoc;
6566 use language::{FakeLspAdapter, LanguageConfig};
6567 use lsp::FakeLanguageServer;
6568 use project::FakeFs;
6569 use settings::LanguageOverride;
6570 use smol::stream::StreamExt;
6571 use std::{cell::RefCell, rc::Rc, time::Instant};
6572 use text::Point;
6573 use unindent::Unindent;
6574 use util::test::{marked_text_by, marked_text_ranges, sample_text};
6575 use workspace::{FollowableItem, ItemHandle};
6576
6577 #[gpui::test]
6578 fn test_edit_events(cx: &mut MutableAppContext) {
6579 cx.set_global(Settings::test(cx));
6580 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6581
6582 let events = Rc::new(RefCell::new(Vec::new()));
6583 let (_, editor1) = cx.add_window(Default::default(), {
6584 let events = events.clone();
6585 |cx| {
6586 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6587 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6588 events.borrow_mut().push(("editor1", *event));
6589 }
6590 })
6591 .detach();
6592 Editor::for_buffer(buffer.clone(), None, cx)
6593 }
6594 });
6595 let (_, editor2) = cx.add_window(Default::default(), {
6596 let events = events.clone();
6597 |cx| {
6598 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6599 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6600 events.borrow_mut().push(("editor2", *event));
6601 }
6602 })
6603 .detach();
6604 Editor::for_buffer(buffer.clone(), None, cx)
6605 }
6606 });
6607 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6608
6609 // Mutating editor 1 will emit an `Edited` event only for that editor.
6610 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6611 assert_eq!(
6612 mem::take(&mut *events.borrow_mut()),
6613 [
6614 ("editor1", Event::Edited),
6615 ("editor1", Event::BufferEdited),
6616 ("editor2", Event::BufferEdited),
6617 ("editor1", Event::Dirtied),
6618 ("editor2", Event::Dirtied)
6619 ]
6620 );
6621
6622 // Mutating editor 2 will emit an `Edited` event only for that editor.
6623 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6624 assert_eq!(
6625 mem::take(&mut *events.borrow_mut()),
6626 [
6627 ("editor2", Event::Edited),
6628 ("editor1", Event::BufferEdited),
6629 ("editor2", Event::BufferEdited),
6630 ]
6631 );
6632
6633 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6634 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6635 assert_eq!(
6636 mem::take(&mut *events.borrow_mut()),
6637 [
6638 ("editor1", Event::Edited),
6639 ("editor1", Event::BufferEdited),
6640 ("editor2", Event::BufferEdited),
6641 ]
6642 );
6643
6644 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6645 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6646 assert_eq!(
6647 mem::take(&mut *events.borrow_mut()),
6648 [
6649 ("editor1", Event::Edited),
6650 ("editor1", Event::BufferEdited),
6651 ("editor2", Event::BufferEdited),
6652 ]
6653 );
6654
6655 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6656 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6657 assert_eq!(
6658 mem::take(&mut *events.borrow_mut()),
6659 [
6660 ("editor2", Event::Edited),
6661 ("editor1", Event::BufferEdited),
6662 ("editor2", Event::BufferEdited),
6663 ]
6664 );
6665
6666 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6667 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6668 assert_eq!(
6669 mem::take(&mut *events.borrow_mut()),
6670 [
6671 ("editor2", Event::Edited),
6672 ("editor1", Event::BufferEdited),
6673 ("editor2", Event::BufferEdited),
6674 ]
6675 );
6676
6677 // No event is emitted when the mutation is a no-op.
6678 editor2.update(cx, |editor, cx| {
6679 editor.select_ranges([0..0], None, cx);
6680 editor.backspace(&Backspace, cx);
6681 });
6682 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6683 }
6684
6685 #[gpui::test]
6686 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6687 cx.set_global(Settings::test(cx));
6688 let mut now = Instant::now();
6689 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6690 let group_interval = buffer.read(cx).transaction_group_interval();
6691 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6692 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6693
6694 editor.update(cx, |editor, cx| {
6695 editor.start_transaction_at(now, cx);
6696 editor.select_ranges([2..4], None, cx);
6697 editor.insert("cd", cx);
6698 editor.end_transaction_at(now, cx);
6699 assert_eq!(editor.text(cx), "12cd56");
6700 assert_eq!(editor.selected_ranges(cx), vec![4..4]);
6701
6702 editor.start_transaction_at(now, cx);
6703 editor.select_ranges([4..5], None, cx);
6704 editor.insert("e", cx);
6705 editor.end_transaction_at(now, cx);
6706 assert_eq!(editor.text(cx), "12cde6");
6707 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
6708
6709 now += group_interval + Duration::from_millis(1);
6710 editor.select_ranges([2..2], None, cx);
6711
6712 // Simulate an edit in another editor
6713 buffer.update(cx, |buffer, cx| {
6714 buffer.start_transaction_at(now, cx);
6715 buffer.edit([(0..1, "a")], cx);
6716 buffer.edit([(1..1, "b")], cx);
6717 buffer.end_transaction_at(now, cx);
6718 });
6719
6720 assert_eq!(editor.text(cx), "ab2cde6");
6721 assert_eq!(editor.selected_ranges(cx), vec![3..3]);
6722
6723 // Last transaction happened past the group interval in a different editor.
6724 // Undo it individually and don't restore selections.
6725 editor.undo(&Undo, cx);
6726 assert_eq!(editor.text(cx), "12cde6");
6727 assert_eq!(editor.selected_ranges(cx), vec![2..2]);
6728
6729 // First two transactions happened within the group interval in this editor.
6730 // Undo them together and restore selections.
6731 editor.undo(&Undo, cx);
6732 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6733 assert_eq!(editor.text(cx), "123456");
6734 assert_eq!(editor.selected_ranges(cx), vec![0..0]);
6735
6736 // Redo the first two transactions together.
6737 editor.redo(&Redo, cx);
6738 assert_eq!(editor.text(cx), "12cde6");
6739 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
6740
6741 // Redo the last transaction on its own.
6742 editor.redo(&Redo, cx);
6743 assert_eq!(editor.text(cx), "ab2cde6");
6744 assert_eq!(editor.selected_ranges(cx), vec![6..6]);
6745
6746 // Test empty transactions.
6747 editor.start_transaction_at(now, cx);
6748 editor.end_transaction_at(now, cx);
6749 editor.undo(&Undo, cx);
6750 assert_eq!(editor.text(cx), "12cde6");
6751 });
6752 }
6753
6754 #[gpui::test]
6755 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6756 cx.set_global(Settings::test(cx));
6757
6758 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6759 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6760 editor.update(cx, |view, cx| {
6761 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6762 });
6763 assert_eq!(
6764 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6765 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6766 );
6767
6768 editor.update(cx, |view, cx| {
6769 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6770 });
6771
6772 assert_eq!(
6773 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6774 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6775 );
6776
6777 editor.update(cx, |view, cx| {
6778 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6779 });
6780
6781 assert_eq!(
6782 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6783 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6784 );
6785
6786 editor.update(cx, |view, cx| {
6787 view.end_selection(cx);
6788 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6789 });
6790
6791 assert_eq!(
6792 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6793 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6794 );
6795
6796 editor.update(cx, |view, cx| {
6797 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6798 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6799 });
6800
6801 assert_eq!(
6802 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6803 [
6804 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6805 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6806 ]
6807 );
6808
6809 editor.update(cx, |view, cx| {
6810 view.end_selection(cx);
6811 });
6812
6813 assert_eq!(
6814 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6815 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6816 );
6817 }
6818
6819 #[gpui::test]
6820 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6821 cx.set_global(Settings::test(cx));
6822 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6823 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6824
6825 view.update(cx, |view, cx| {
6826 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6827 assert_eq!(
6828 view.selected_display_ranges(cx),
6829 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6830 );
6831 });
6832
6833 view.update(cx, |view, cx| {
6834 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6835 assert_eq!(
6836 view.selected_display_ranges(cx),
6837 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6838 );
6839 });
6840
6841 view.update(cx, |view, cx| {
6842 view.cancel(&Cancel, cx);
6843 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6844 assert_eq!(
6845 view.selected_display_ranges(cx),
6846 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6847 );
6848 });
6849 }
6850
6851 #[gpui::test]
6852 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6853 cx.set_global(Settings::test(cx));
6854 use workspace::Item;
6855 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6856 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6857
6858 cx.add_window(Default::default(), |cx| {
6859 let mut editor = build_editor(buffer.clone(), cx);
6860 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6861
6862 // Move the cursor a small distance.
6863 // Nothing is added to the navigation history.
6864 editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
6865 editor.select_display_ranges(&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)], cx);
6866 assert!(nav_history.borrow_mut().pop_backward().is_none());
6867
6868 // Move the cursor a large distance.
6869 // The history can jump back to the previous position.
6870 editor.select_display_ranges(&[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)], cx);
6871 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6872 editor.navigate(nav_entry.data.unwrap(), cx);
6873 assert_eq!(nav_entry.item.id(), cx.view_id());
6874 assert_eq!(
6875 editor.selected_display_ranges(cx),
6876 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6877 );
6878 assert!(nav_history.borrow_mut().pop_backward().is_none());
6879
6880 // Move the cursor a small distance via the mouse.
6881 // Nothing is added to the navigation history.
6882 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6883 editor.end_selection(cx);
6884 assert_eq!(
6885 editor.selected_display_ranges(cx),
6886 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6887 );
6888 assert!(nav_history.borrow_mut().pop_backward().is_none());
6889
6890 // Move the cursor a large distance via the mouse.
6891 // The history can jump back to the previous position.
6892 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6893 editor.end_selection(cx);
6894 assert_eq!(
6895 editor.selected_display_ranges(cx),
6896 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6897 );
6898 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6899 editor.navigate(nav_entry.data.unwrap(), cx);
6900 assert_eq!(nav_entry.item.id(), cx.view_id());
6901 assert_eq!(
6902 editor.selected_display_ranges(cx),
6903 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6904 );
6905 assert!(nav_history.borrow_mut().pop_backward().is_none());
6906
6907 // Set scroll position to check later
6908 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6909 let original_scroll_position = editor.scroll_position;
6910 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6911
6912 // Jump to the end of the document and adjust scroll
6913 editor.move_to_end(&MoveToEnd, cx);
6914 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6915 assert_ne!(editor.scroll_position, original_scroll_position);
6916 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6917
6918 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6919 editor.navigate(nav_entry.data.unwrap(), cx);
6920 assert_eq!(editor.scroll_position, original_scroll_position);
6921 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6922
6923 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6924 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6925 invalid_anchor.text_anchor.buffer_id = Some(999);
6926 let invalid_point = Point::new(9999, 0);
6927 editor.navigate(
6928 Box::new(NavigationData {
6929 cursor_anchor: invalid_anchor.clone(),
6930 cursor_position: invalid_point,
6931 scroll_top_anchor: invalid_anchor.clone(),
6932 scroll_top_row: invalid_point.row,
6933 scroll_position: Default::default(),
6934 }),
6935 cx,
6936 );
6937 assert_eq!(
6938 editor.selected_display_ranges(cx),
6939 &[editor.max_point(cx)..editor.max_point(cx)]
6940 );
6941 assert_eq!(
6942 editor.scroll_position(cx),
6943 vec2f(0., editor.max_point(cx).row() as f32)
6944 );
6945
6946 editor
6947 });
6948 }
6949
6950 #[gpui::test]
6951 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6952 cx.set_global(Settings::test(cx));
6953 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6954 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6955
6956 view.update(cx, |view, cx| {
6957 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6958 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6959 view.end_selection(cx);
6960
6961 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6962 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6963 view.end_selection(cx);
6964 assert_eq!(
6965 view.selected_display_ranges(cx),
6966 [
6967 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6968 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6969 ]
6970 );
6971 });
6972
6973 view.update(cx, |view, cx| {
6974 view.cancel(&Cancel, cx);
6975 assert_eq!(
6976 view.selected_display_ranges(cx),
6977 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6978 );
6979 });
6980
6981 view.update(cx, |view, cx| {
6982 view.cancel(&Cancel, cx);
6983 assert_eq!(
6984 view.selected_display_ranges(cx),
6985 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6986 );
6987 });
6988 }
6989
6990 #[gpui::test]
6991 fn test_fold(cx: &mut gpui::MutableAppContext) {
6992 cx.set_global(Settings::test(cx));
6993 let buffer = MultiBuffer::build_simple(
6994 &"
6995 impl Foo {
6996 // Hello!
6997
6998 fn a() {
6999 1
7000 }
7001
7002 fn b() {
7003 2
7004 }
7005
7006 fn c() {
7007 3
7008 }
7009 }
7010 "
7011 .unindent(),
7012 cx,
7013 );
7014 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7015
7016 view.update(cx, |view, cx| {
7017 view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], cx);
7018 view.fold(&Fold, cx);
7019 assert_eq!(
7020 view.display_text(cx),
7021 "
7022 impl Foo {
7023 // Hello!
7024
7025 fn a() {
7026 1
7027 }
7028
7029 fn b() {…
7030 }
7031
7032 fn c() {…
7033 }
7034 }
7035 "
7036 .unindent(),
7037 );
7038
7039 view.fold(&Fold, cx);
7040 assert_eq!(
7041 view.display_text(cx),
7042 "
7043 impl Foo {…
7044 }
7045 "
7046 .unindent(),
7047 );
7048
7049 view.unfold_lines(&UnfoldLines, cx);
7050 assert_eq!(
7051 view.display_text(cx),
7052 "
7053 impl Foo {
7054 // Hello!
7055
7056 fn a() {
7057 1
7058 }
7059
7060 fn b() {…
7061 }
7062
7063 fn c() {…
7064 }
7065 }
7066 "
7067 .unindent(),
7068 );
7069
7070 view.unfold_lines(&UnfoldLines, cx);
7071 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
7072 });
7073 }
7074
7075 #[gpui::test]
7076 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
7077 cx.set_global(Settings::test(cx));
7078 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
7079 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7080
7081 buffer.update(cx, |buffer, cx| {
7082 buffer.edit(
7083 vec![
7084 (Point::new(1, 0)..Point::new(1, 0), "\t"),
7085 (Point::new(1, 1)..Point::new(1, 1), "\t"),
7086 ],
7087 cx,
7088 );
7089 });
7090
7091 view.update(cx, |view, cx| {
7092 assert_eq!(
7093 view.selected_display_ranges(cx),
7094 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7095 );
7096
7097 view.move_down(&MoveDown, cx);
7098 assert_eq!(
7099 view.selected_display_ranges(cx),
7100 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7101 );
7102
7103 view.move_right(&MoveRight, cx);
7104 assert_eq!(
7105 view.selected_display_ranges(cx),
7106 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
7107 );
7108
7109 view.move_left(&MoveLeft, cx);
7110 assert_eq!(
7111 view.selected_display_ranges(cx),
7112 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
7113 );
7114
7115 view.move_up(&MoveUp, cx);
7116 assert_eq!(
7117 view.selected_display_ranges(cx),
7118 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7119 );
7120
7121 view.move_to_end(&MoveToEnd, cx);
7122 assert_eq!(
7123 view.selected_display_ranges(cx),
7124 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
7125 );
7126
7127 view.move_to_beginning(&MoveToBeginning, cx);
7128 assert_eq!(
7129 view.selected_display_ranges(cx),
7130 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
7131 );
7132
7133 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], cx);
7134 view.select_to_beginning(&SelectToBeginning, cx);
7135 assert_eq!(
7136 view.selected_display_ranges(cx),
7137 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
7138 );
7139
7140 view.select_to_end(&SelectToEnd, cx);
7141 assert_eq!(
7142 view.selected_display_ranges(cx),
7143 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
7144 );
7145 });
7146 }
7147
7148 #[gpui::test]
7149 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
7150 cx.set_global(Settings::test(cx));
7151 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
7152 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7153
7154 assert_eq!('ⓐ'.len_utf8(), 3);
7155 assert_eq!('α'.len_utf8(), 2);
7156
7157 view.update(cx, |view, cx| {
7158 view.fold_ranges(
7159 vec![
7160 Point::new(0, 6)..Point::new(0, 12),
7161 Point::new(1, 2)..Point::new(1, 4),
7162 Point::new(2, 4)..Point::new(2, 8),
7163 ],
7164 cx,
7165 );
7166 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
7167
7168 view.move_right(&MoveRight, cx);
7169 assert_eq!(
7170 view.selected_display_ranges(cx),
7171 &[empty_range(0, "ⓐ".len())]
7172 );
7173 view.move_right(&MoveRight, cx);
7174 assert_eq!(
7175 view.selected_display_ranges(cx),
7176 &[empty_range(0, "ⓐⓑ".len())]
7177 );
7178 view.move_right(&MoveRight, cx);
7179 assert_eq!(
7180 view.selected_display_ranges(cx),
7181 &[empty_range(0, "ⓐⓑ…".len())]
7182 );
7183
7184 view.move_down(&MoveDown, cx);
7185 assert_eq!(
7186 view.selected_display_ranges(cx),
7187 &[empty_range(1, "ab…".len())]
7188 );
7189 view.move_left(&MoveLeft, cx);
7190 assert_eq!(
7191 view.selected_display_ranges(cx),
7192 &[empty_range(1, "ab".len())]
7193 );
7194 view.move_left(&MoveLeft, cx);
7195 assert_eq!(
7196 view.selected_display_ranges(cx),
7197 &[empty_range(1, "a".len())]
7198 );
7199
7200 view.move_down(&MoveDown, cx);
7201 assert_eq!(
7202 view.selected_display_ranges(cx),
7203 &[empty_range(2, "α".len())]
7204 );
7205 view.move_right(&MoveRight, cx);
7206 assert_eq!(
7207 view.selected_display_ranges(cx),
7208 &[empty_range(2, "αβ".len())]
7209 );
7210 view.move_right(&MoveRight, cx);
7211 assert_eq!(
7212 view.selected_display_ranges(cx),
7213 &[empty_range(2, "αβ…".len())]
7214 );
7215 view.move_right(&MoveRight, cx);
7216 assert_eq!(
7217 view.selected_display_ranges(cx),
7218 &[empty_range(2, "αβ…ε".len())]
7219 );
7220
7221 view.move_up(&MoveUp, cx);
7222 assert_eq!(
7223 view.selected_display_ranges(cx),
7224 &[empty_range(1, "ab…e".len())]
7225 );
7226 view.move_up(&MoveUp, cx);
7227 assert_eq!(
7228 view.selected_display_ranges(cx),
7229 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
7230 );
7231 view.move_left(&MoveLeft, cx);
7232 assert_eq!(
7233 view.selected_display_ranges(cx),
7234 &[empty_range(0, "ⓐⓑ…".len())]
7235 );
7236 view.move_left(&MoveLeft, cx);
7237 assert_eq!(
7238 view.selected_display_ranges(cx),
7239 &[empty_range(0, "ⓐⓑ".len())]
7240 );
7241 view.move_left(&MoveLeft, cx);
7242 assert_eq!(
7243 view.selected_display_ranges(cx),
7244 &[empty_range(0, "ⓐ".len())]
7245 );
7246 });
7247 }
7248
7249 #[gpui::test]
7250 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
7251 cx.set_global(Settings::test(cx));
7252 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7253 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7254 view.update(cx, |view, cx| {
7255 view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx);
7256 view.move_down(&MoveDown, cx);
7257 assert_eq!(
7258 view.selected_display_ranges(cx),
7259 &[empty_range(1, "abcd".len())]
7260 );
7261
7262 view.move_down(&MoveDown, cx);
7263 assert_eq!(
7264 view.selected_display_ranges(cx),
7265 &[empty_range(2, "αβγ".len())]
7266 );
7267
7268 view.move_down(&MoveDown, cx);
7269 assert_eq!(
7270 view.selected_display_ranges(cx),
7271 &[empty_range(3, "abcd".len())]
7272 );
7273
7274 view.move_down(&MoveDown, cx);
7275 assert_eq!(
7276 view.selected_display_ranges(cx),
7277 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7278 );
7279
7280 view.move_up(&MoveUp, cx);
7281 assert_eq!(
7282 view.selected_display_ranges(cx),
7283 &[empty_range(3, "abcd".len())]
7284 );
7285
7286 view.move_up(&MoveUp, cx);
7287 assert_eq!(
7288 view.selected_display_ranges(cx),
7289 &[empty_range(2, "αβγ".len())]
7290 );
7291 });
7292 }
7293
7294 #[gpui::test]
7295 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7296 cx.set_global(Settings::test(cx));
7297 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7298 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7299 view.update(cx, |view, cx| {
7300 view.select_display_ranges(
7301 &[
7302 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7303 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7304 ],
7305 cx,
7306 );
7307 });
7308
7309 view.update(cx, |view, cx| {
7310 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7311 assert_eq!(
7312 view.selected_display_ranges(cx),
7313 &[
7314 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7315 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7316 ]
7317 );
7318 });
7319
7320 view.update(cx, |view, cx| {
7321 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7322 assert_eq!(
7323 view.selected_display_ranges(cx),
7324 &[
7325 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7326 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7327 ]
7328 );
7329 });
7330
7331 view.update(cx, |view, cx| {
7332 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7333 assert_eq!(
7334 view.selected_display_ranges(cx),
7335 &[
7336 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7337 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7338 ]
7339 );
7340 });
7341
7342 view.update(cx, |view, cx| {
7343 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7344 assert_eq!(
7345 view.selected_display_ranges(cx),
7346 &[
7347 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7348 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7349 ]
7350 );
7351 });
7352
7353 // Moving to the end of line again is a no-op.
7354 view.update(cx, |view, cx| {
7355 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7356 assert_eq!(
7357 view.selected_display_ranges(cx),
7358 &[
7359 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7360 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7361 ]
7362 );
7363 });
7364
7365 view.update(cx, |view, cx| {
7366 view.move_left(&MoveLeft, cx);
7367 view.select_to_beginning_of_line(
7368 &SelectToBeginningOfLine {
7369 stop_at_soft_wraps: true,
7370 },
7371 cx,
7372 );
7373 assert_eq!(
7374 view.selected_display_ranges(cx),
7375 &[
7376 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7377 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7378 ]
7379 );
7380 });
7381
7382 view.update(cx, |view, cx| {
7383 view.select_to_beginning_of_line(
7384 &SelectToBeginningOfLine {
7385 stop_at_soft_wraps: true,
7386 },
7387 cx,
7388 );
7389 assert_eq!(
7390 view.selected_display_ranges(cx),
7391 &[
7392 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7393 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7394 ]
7395 );
7396 });
7397
7398 view.update(cx, |view, cx| {
7399 view.select_to_beginning_of_line(
7400 &SelectToBeginningOfLine {
7401 stop_at_soft_wraps: true,
7402 },
7403 cx,
7404 );
7405 assert_eq!(
7406 view.selected_display_ranges(cx),
7407 &[
7408 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7409 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7410 ]
7411 );
7412 });
7413
7414 view.update(cx, |view, cx| {
7415 view.select_to_end_of_line(
7416 &SelectToEndOfLine {
7417 stop_at_soft_wraps: true,
7418 },
7419 cx,
7420 );
7421 assert_eq!(
7422 view.selected_display_ranges(cx),
7423 &[
7424 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7425 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7426 ]
7427 );
7428 });
7429
7430 view.update(cx, |view, cx| {
7431 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7432 assert_eq!(view.display_text(cx), "ab\n de");
7433 assert_eq!(
7434 view.selected_display_ranges(cx),
7435 &[
7436 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7437 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7438 ]
7439 );
7440 });
7441
7442 view.update(cx, |view, cx| {
7443 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7444 assert_eq!(view.display_text(cx), "\n");
7445 assert_eq!(
7446 view.selected_display_ranges(cx),
7447 &[
7448 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7449 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7450 ]
7451 );
7452 });
7453 }
7454
7455 #[gpui::test]
7456 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7457 cx.set_global(Settings::test(cx));
7458 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7459 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7460 view.update(cx, |view, cx| {
7461 view.select_display_ranges(
7462 &[
7463 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7464 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7465 ],
7466 cx,
7467 );
7468
7469 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7470 assert_selection_ranges(
7471 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7472 vec![('<', '>'), ('[', ']')],
7473 view,
7474 cx,
7475 );
7476
7477 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7478 assert_selection_ranges(
7479 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7480 vec![('<', '>'), ('[', ']')],
7481 view,
7482 cx,
7483 );
7484
7485 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7486 assert_selection_ranges(
7487 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7488 vec![('<', '>'), ('[', ']')],
7489 view,
7490 cx,
7491 );
7492
7493 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7494 assert_selection_ranges(
7495 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7496 vec![('<', '>'), ('[', ']')],
7497 view,
7498 cx,
7499 );
7500
7501 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7502 assert_selection_ranges(
7503 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7504 vec![('<', '>'), ('[', ']')],
7505 view,
7506 cx,
7507 );
7508
7509 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7510 assert_selection_ranges(
7511 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7512 vec![('<', '>'), ('[', ']')],
7513 view,
7514 cx,
7515 );
7516
7517 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7518 assert_selection_ranges(
7519 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7520 vec![('<', '>'), ('[', ']')],
7521 view,
7522 cx,
7523 );
7524
7525 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7526 assert_selection_ranges(
7527 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7528 vec![('<', '>'), ('[', ']')],
7529 view,
7530 cx,
7531 );
7532
7533 view.move_right(&MoveRight, cx);
7534 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7535 assert_selection_ranges(
7536 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7537 vec![('<', '>'), ('[', ']')],
7538 view,
7539 cx,
7540 );
7541
7542 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7543 assert_selection_ranges(
7544 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7545 vec![('<', '>'), ('[', ']')],
7546 view,
7547 cx,
7548 );
7549
7550 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7551 assert_selection_ranges(
7552 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7553 vec![('<', '>'), ('[', ']')],
7554 view,
7555 cx,
7556 );
7557 });
7558 }
7559
7560 #[gpui::test]
7561 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7562 cx.set_global(Settings::test(cx));
7563 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7564 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7565
7566 view.update(cx, |view, cx| {
7567 view.set_wrap_width(Some(140.), cx);
7568 assert_eq!(
7569 view.display_text(cx),
7570 "use one::{\n two::three::\n four::five\n};"
7571 );
7572
7573 view.select_display_ranges(&[DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)], cx);
7574
7575 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7576 assert_eq!(
7577 view.selected_display_ranges(cx),
7578 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7579 );
7580
7581 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7582 assert_eq!(
7583 view.selected_display_ranges(cx),
7584 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7585 );
7586
7587 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7588 assert_eq!(
7589 view.selected_display_ranges(cx),
7590 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7591 );
7592
7593 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7594 assert_eq!(
7595 view.selected_display_ranges(cx),
7596 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7597 );
7598
7599 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7600 assert_eq!(
7601 view.selected_display_ranges(cx),
7602 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7603 );
7604
7605 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7606 assert_eq!(
7607 view.selected_display_ranges(cx),
7608 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7609 );
7610 });
7611 }
7612
7613 #[gpui::test]
7614 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7615 cx.set_global(Settings::test(cx));
7616 let (text, ranges) = marked_text_ranges("one [two three] four");
7617 let buffer = MultiBuffer::build_simple(&text, cx);
7618
7619 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7620
7621 editor.update(cx, |editor, cx| {
7622 editor.select_ranges(ranges, None, cx);
7623 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7624 assert_eq!(editor.text(cx), " four");
7625 });
7626 }
7627
7628 #[gpui::test]
7629 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7630 cx.set_global(Settings::test(cx));
7631 let buffer = MultiBuffer::build_simple("one two three four", cx);
7632 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7633
7634 view.update(cx, |view, cx| {
7635 view.select_display_ranges(
7636 &[
7637 // an empty selection - the preceding word fragment is deleted
7638 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7639 // characters selected - they are deleted
7640 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7641 ],
7642 cx,
7643 );
7644 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7645 });
7646
7647 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7648
7649 view.update(cx, |view, cx| {
7650 view.select_display_ranges(
7651 &[
7652 // an empty selection - the following word fragment is deleted
7653 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7654 // characters selected - they are deleted
7655 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7656 ],
7657 cx,
7658 );
7659 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7660 });
7661
7662 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7663 }
7664
7665 #[gpui::test]
7666 fn test_newline(cx: &mut gpui::MutableAppContext) {
7667 cx.set_global(Settings::test(cx));
7668 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7669 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7670
7671 view.update(cx, |view, cx| {
7672 view.select_display_ranges(
7673 &[
7674 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7675 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7676 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7677 ],
7678 cx,
7679 );
7680
7681 view.newline(&Newline, cx);
7682 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7683 });
7684 }
7685
7686 #[gpui::test]
7687 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7688 cx.set_global(Settings::test(cx));
7689 let buffer = MultiBuffer::build_simple(
7690 "
7691 a
7692 b(
7693 X
7694 )
7695 c(
7696 X
7697 )
7698 "
7699 .unindent()
7700 .as_str(),
7701 cx,
7702 );
7703
7704 let (_, editor) = cx.add_window(Default::default(), |cx| {
7705 let mut editor = build_editor(buffer.clone(), cx);
7706 editor.select_ranges(
7707 [
7708 Point::new(2, 4)..Point::new(2, 5),
7709 Point::new(5, 4)..Point::new(5, 5),
7710 ],
7711 None,
7712 cx,
7713 );
7714 editor
7715 });
7716
7717 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7718 buffer.update(cx, |buffer, cx| {
7719 buffer.edit(
7720 [
7721 (Point::new(1, 2)..Point::new(3, 0), ""),
7722 (Point::new(4, 2)..Point::new(6, 0), ""),
7723 ],
7724 cx,
7725 );
7726 assert_eq!(
7727 buffer.read(cx).text(),
7728 "
7729 a
7730 b()
7731 c()
7732 "
7733 .unindent()
7734 );
7735 });
7736
7737 editor.update(cx, |editor, cx| {
7738 assert_eq!(
7739 editor.selected_ranges(cx),
7740 &[
7741 Point::new(1, 2)..Point::new(1, 2),
7742 Point::new(2, 2)..Point::new(2, 2),
7743 ],
7744 );
7745
7746 editor.newline(&Newline, cx);
7747 assert_eq!(
7748 editor.text(cx),
7749 "
7750 a
7751 b(
7752 )
7753 c(
7754 )
7755 "
7756 .unindent()
7757 );
7758
7759 // The selections are moved after the inserted newlines
7760 assert_eq!(
7761 editor.selected_ranges(cx),
7762 &[
7763 Point::new(2, 0)..Point::new(2, 0),
7764 Point::new(4, 0)..Point::new(4, 0),
7765 ],
7766 );
7767 });
7768 }
7769
7770 #[gpui::test]
7771 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7772 cx.set_global(Settings::test(cx));
7773 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7774 let (_, editor) = cx.add_window(Default::default(), |cx| {
7775 let mut editor = build_editor(buffer.clone(), cx);
7776 editor.select_ranges([3..4, 11..12, 19..20], None, cx);
7777 editor
7778 });
7779
7780 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7781 buffer.update(cx, |buffer, cx| {
7782 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7783 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7784 });
7785
7786 editor.update(cx, |editor, cx| {
7787 assert_eq!(editor.selected_ranges(cx), &[2..2, 7..7, 12..12],);
7788
7789 editor.insert("Z", cx);
7790 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7791
7792 // The selections are moved after the inserted characters
7793 assert_eq!(editor.selected_ranges(cx), &[3..3, 9..9, 15..15],);
7794 });
7795 }
7796
7797 #[gpui::test]
7798 fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
7799 cx.set_global(Settings::test(cx));
7800 let buffer = MultiBuffer::build_simple(
7801 indoc! {"
7802 one two
7803 three
7804 four"},
7805 cx,
7806 );
7807 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7808
7809 view.update(cx, |view, cx| {
7810 // two selections on the same line
7811 select_ranges(
7812 view,
7813 indoc! {"
7814 [one] [two]
7815 three
7816 four"},
7817 cx,
7818 );
7819
7820 // indent from mid-tabstop to full tabstop
7821 view.tab(&Tab, cx);
7822 assert_text_with_selections(
7823 view,
7824 indoc! {"
7825 [one] [two]
7826 three
7827 four"},
7828 cx,
7829 );
7830
7831 // outdent from 1 tabstop to 0 tabstops
7832 view.tab_prev(&TabPrev, cx);
7833 assert_text_with_selections(
7834 view,
7835 indoc! {"
7836 [one] [two]
7837 three
7838 four"},
7839 cx,
7840 );
7841
7842 // select across line ending
7843 select_ranges(
7844 view,
7845 indoc! {"
7846 one two
7847 t[hree
7848 ] four"},
7849 cx,
7850 );
7851
7852 // indent and outdent affect only the preceding line
7853 view.tab(&Tab, cx);
7854 assert_text_with_selections(
7855 view,
7856 indoc! {"
7857 one two
7858 t[hree
7859 ] four"},
7860 cx,
7861 );
7862 view.tab_prev(&TabPrev, cx);
7863 assert_text_with_selections(
7864 view,
7865 indoc! {"
7866 one two
7867 t[hree
7868 ] four"},
7869 cx,
7870 );
7871
7872 // Ensure that indenting/outdenting works when the cursor is at column 0.
7873 select_ranges(
7874 view,
7875 indoc! {"
7876 one two
7877 []three
7878 four"},
7879 cx,
7880 );
7881 view.tab(&Tab, cx);
7882 assert_text_with_selections(
7883 view,
7884 indoc! {"
7885 one two
7886 []three
7887 four"},
7888 cx,
7889 );
7890
7891 select_ranges(
7892 view,
7893 indoc! {"
7894 one two
7895 [] three
7896 four"},
7897 cx,
7898 );
7899 view.tab_prev(&TabPrev, cx);
7900 assert_text_with_selections(
7901 view,
7902 indoc! {"
7903 one two
7904 []three
7905 four"},
7906 cx,
7907 );
7908 });
7909 }
7910
7911 #[gpui::test]
7912 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7913 cx.set_global(
7914 Settings::test(cx)
7915 .with_overrides(
7916 "TOML",
7917 LanguageOverride {
7918 tab_size: Some(2),
7919 ..Default::default()
7920 },
7921 )
7922 .with_overrides(
7923 "Rust",
7924 LanguageOverride {
7925 tab_size: Some(4),
7926 ..Default::default()
7927 },
7928 ),
7929 );
7930 let toml_language = Arc::new(Language::new(
7931 LanguageConfig {
7932 name: "TOML".into(),
7933 ..Default::default()
7934 },
7935 None,
7936 ));
7937 let rust_language = Arc::new(Language::new(
7938 LanguageConfig {
7939 name: "Rust".into(),
7940 ..Default::default()
7941 },
7942 None,
7943 ));
7944
7945 let toml_buffer = cx
7946 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7947 let rust_buffer = cx.add_model(|cx| {
7948 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7949 });
7950 let multibuffer = cx.add_model(|cx| {
7951 let mut multibuffer = MultiBuffer::new(0);
7952 multibuffer.push_excerpts(
7953 toml_buffer.clone(),
7954 [Point::new(0, 0)..Point::new(2, 0)],
7955 cx,
7956 );
7957 multibuffer.push_excerpts(
7958 rust_buffer.clone(),
7959 [Point::new(0, 0)..Point::new(1, 0)],
7960 cx,
7961 );
7962 multibuffer
7963 });
7964
7965 cx.add_window(Default::default(), |cx| {
7966 let mut editor = build_editor(multibuffer, cx);
7967
7968 assert_eq!(
7969 editor.text(cx),
7970 indoc! {"
7971 a = 1
7972 b = 2
7973
7974 const c: usize = 3;
7975 "}
7976 );
7977
7978 select_ranges(
7979 &mut editor,
7980 indoc! {"
7981 [a] = 1
7982 b = 2
7983
7984 [const c:] usize = 3;
7985 "},
7986 cx,
7987 );
7988
7989 editor.tab(&Tab, cx);
7990 assert_text_with_selections(
7991 &mut editor,
7992 indoc! {"
7993 [a] = 1
7994 b = 2
7995
7996 [const c:] usize = 3;
7997 "},
7998 cx,
7999 );
8000 editor.tab_prev(&TabPrev, cx);
8001 assert_text_with_selections(
8002 &mut editor,
8003 indoc! {"
8004 [a] = 1
8005 b = 2
8006
8007 [const c:] usize = 3;
8008 "},
8009 cx,
8010 );
8011
8012 editor
8013 });
8014 }
8015
8016 #[gpui::test]
8017 fn test_backspace(cx: &mut gpui::MutableAppContext) {
8018 cx.set_global(Settings::test(cx));
8019 let (_, view) = cx.add_window(Default::default(), |cx| {
8020 build_editor(MultiBuffer::build_simple("", cx), cx)
8021 });
8022
8023 view.update(cx, |view, cx| {
8024 view.set_text("one two three\nfour five six\nseven eight nine\nten\n", cx);
8025 view.select_display_ranges(
8026 &[
8027 // an empty selection - the preceding character is deleted
8028 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8029 // one character selected - it is deleted
8030 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8031 // a line suffix selected - it is deleted
8032 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
8033 ],
8034 cx,
8035 );
8036 view.backspace(&Backspace, cx);
8037 assert_eq!(view.text(cx), "oe two three\nfou five six\nseven ten\n");
8038
8039 view.set_text(" one\n two\n three\n four", cx);
8040 view.select_display_ranges(
8041 &[
8042 // cursors at the the end of leading indent - last indent is deleted
8043 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
8044 DisplayPoint::new(1, 8)..DisplayPoint::new(1, 8),
8045 // cursors inside leading indent - overlapping indent deletions are coalesced
8046 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
8047 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8048 DisplayPoint::new(2, 6)..DisplayPoint::new(2, 6),
8049 // cursor at the beginning of a line - preceding newline is deleted
8050 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8051 // selection inside leading indent - only the selected character is deleted
8052 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3),
8053 ],
8054 cx,
8055 );
8056 view.backspace(&Backspace, cx);
8057 assert_eq!(view.text(cx), "one\n two\n three four");
8058 });
8059 }
8060
8061 #[gpui::test]
8062 fn test_delete(cx: &mut gpui::MutableAppContext) {
8063 cx.set_global(Settings::test(cx));
8064 let buffer =
8065 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
8066 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
8067
8068 view.update(cx, |view, cx| {
8069 view.select_display_ranges(
8070 &[
8071 // an empty selection - the following character is deleted
8072 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8073 // one character selected - it is deleted
8074 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8075 // a line suffix selected - it is deleted
8076 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
8077 ],
8078 cx,
8079 );
8080 view.delete(&Delete, cx);
8081 });
8082
8083 assert_eq!(
8084 buffer.read(cx).read(cx).text(),
8085 "on two three\nfou five six\nseven ten\n"
8086 );
8087 }
8088
8089 #[gpui::test]
8090 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
8091 cx.set_global(Settings::test(cx));
8092 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8093 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8094 view.update(cx, |view, cx| {
8095 view.select_display_ranges(
8096 &[
8097 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8098 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8099 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8100 ],
8101 cx,
8102 );
8103 view.delete_line(&DeleteLine, cx);
8104 assert_eq!(view.display_text(cx), "ghi");
8105 assert_eq!(
8106 view.selected_display_ranges(cx),
8107 vec![
8108 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
8109 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
8110 ]
8111 );
8112 });
8113
8114 cx.set_global(Settings::test(cx));
8115 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8116 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8117 view.update(cx, |view, cx| {
8118 view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx);
8119 view.delete_line(&DeleteLine, cx);
8120 assert_eq!(view.display_text(cx), "ghi\n");
8121 assert_eq!(
8122 view.selected_display_ranges(cx),
8123 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
8124 );
8125 });
8126 }
8127
8128 #[gpui::test]
8129 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
8130 cx.set_global(Settings::test(cx));
8131 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8132 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8133 view.update(cx, |view, cx| {
8134 view.select_display_ranges(
8135 &[
8136 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8137 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8138 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8139 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8140 ],
8141 cx,
8142 );
8143 view.duplicate_line(&DuplicateLine, cx);
8144 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
8145 assert_eq!(
8146 view.selected_display_ranges(cx),
8147 vec![
8148 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8149 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
8150 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8151 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
8152 ]
8153 );
8154 });
8155
8156 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
8157 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8158 view.update(cx, |view, cx| {
8159 view.select_display_ranges(
8160 &[
8161 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
8162 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
8163 ],
8164 cx,
8165 );
8166 view.duplicate_line(&DuplicateLine, cx);
8167 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
8168 assert_eq!(
8169 view.selected_display_ranges(cx),
8170 vec![
8171 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
8172 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
8173 ]
8174 );
8175 });
8176 }
8177
8178 #[gpui::test]
8179 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
8180 cx.set_global(Settings::test(cx));
8181 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8182 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8183 view.update(cx, |view, cx| {
8184 view.fold_ranges(
8185 vec![
8186 Point::new(0, 2)..Point::new(1, 2),
8187 Point::new(2, 3)..Point::new(4, 1),
8188 Point::new(7, 0)..Point::new(8, 4),
8189 ],
8190 cx,
8191 );
8192 view.select_display_ranges(
8193 &[
8194 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8195 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8196 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8197 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
8198 ],
8199 cx,
8200 );
8201 assert_eq!(
8202 view.display_text(cx),
8203 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
8204 );
8205
8206 view.move_line_up(&MoveLineUp, cx);
8207 assert_eq!(
8208 view.display_text(cx),
8209 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
8210 );
8211 assert_eq!(
8212 view.selected_display_ranges(cx),
8213 vec![
8214 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8215 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8216 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8217 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8218 ]
8219 );
8220 });
8221
8222 view.update(cx, |view, cx| {
8223 view.move_line_down(&MoveLineDown, cx);
8224 assert_eq!(
8225 view.display_text(cx),
8226 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
8227 );
8228 assert_eq!(
8229 view.selected_display_ranges(cx),
8230 vec![
8231 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8232 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8233 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8234 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8235 ]
8236 );
8237 });
8238
8239 view.update(cx, |view, cx| {
8240 view.move_line_down(&MoveLineDown, cx);
8241 assert_eq!(
8242 view.display_text(cx),
8243 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8244 );
8245 assert_eq!(
8246 view.selected_display_ranges(cx),
8247 vec![
8248 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8249 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8250 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8251 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8252 ]
8253 );
8254 });
8255
8256 view.update(cx, |view, cx| {
8257 view.move_line_up(&MoveLineUp, cx);
8258 assert_eq!(
8259 view.display_text(cx),
8260 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8261 );
8262 assert_eq!(
8263 view.selected_display_ranges(cx),
8264 vec![
8265 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8266 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8267 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8268 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8269 ]
8270 );
8271 });
8272 }
8273
8274 #[gpui::test]
8275 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8276 cx.set_global(Settings::test(cx));
8277 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8278 let snapshot = buffer.read(cx).snapshot(cx);
8279 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8280 editor.update(cx, |editor, cx| {
8281 editor.insert_blocks(
8282 [BlockProperties {
8283 position: snapshot.anchor_after(Point::new(2, 0)),
8284 disposition: BlockDisposition::Below,
8285 height: 1,
8286 render: Arc::new(|_| Empty::new().boxed()),
8287 }],
8288 cx,
8289 );
8290 editor.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None, cx);
8291 editor.move_line_down(&MoveLineDown, cx);
8292 });
8293 }
8294
8295 #[gpui::test]
8296 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8297 cx.set_global(Settings::test(cx));
8298
8299 cx.add_window(Default::default(), |cx| {
8300 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8301
8302 editor.select_ranges([1..1], None, cx);
8303 editor.transpose(&Default::default(), cx);
8304 assert_eq!(editor.text(cx), "bac");
8305 assert_eq!(editor.selected_ranges(cx), [2..2]);
8306
8307 editor.transpose(&Default::default(), cx);
8308 assert_eq!(editor.text(cx), "bca");
8309 assert_eq!(editor.selected_ranges(cx), [3..3]);
8310
8311 editor.transpose(&Default::default(), cx);
8312 assert_eq!(editor.text(cx), "bac");
8313 assert_eq!(editor.selected_ranges(cx), [3..3]);
8314
8315 editor
8316 })
8317 .1;
8318
8319 cx.add_window(Default::default(), |cx| {
8320 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8321
8322 editor.select_ranges([3..3], None, cx);
8323 editor.transpose(&Default::default(), cx);
8324 assert_eq!(editor.text(cx), "acb\nde");
8325 assert_eq!(editor.selected_ranges(cx), [3..3]);
8326
8327 editor.select_ranges([4..4], None, cx);
8328 editor.transpose(&Default::default(), cx);
8329 assert_eq!(editor.text(cx), "acbd\ne");
8330 assert_eq!(editor.selected_ranges(cx), [5..5]);
8331
8332 editor.transpose(&Default::default(), cx);
8333 assert_eq!(editor.text(cx), "acbde\n");
8334 assert_eq!(editor.selected_ranges(cx), [6..6]);
8335
8336 editor.transpose(&Default::default(), cx);
8337 assert_eq!(editor.text(cx), "acbd\ne");
8338 assert_eq!(editor.selected_ranges(cx), [6..6]);
8339
8340 editor
8341 })
8342 .1;
8343
8344 cx.add_window(Default::default(), |cx| {
8345 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8346
8347 editor.select_ranges([1..1, 2..2, 4..4], None, cx);
8348 editor.transpose(&Default::default(), cx);
8349 assert_eq!(editor.text(cx), "bacd\ne");
8350 assert_eq!(editor.selected_ranges(cx), [2..2, 3..3, 5..5]);
8351
8352 editor.transpose(&Default::default(), cx);
8353 assert_eq!(editor.text(cx), "bcade\n");
8354 assert_eq!(editor.selected_ranges(cx), [3..3, 4..4, 6..6]);
8355
8356 editor.transpose(&Default::default(), cx);
8357 assert_eq!(editor.text(cx), "bcda\ne");
8358 assert_eq!(editor.selected_ranges(cx), [4..4, 6..6]);
8359
8360 editor.transpose(&Default::default(), cx);
8361 assert_eq!(editor.text(cx), "bcade\n");
8362 assert_eq!(editor.selected_ranges(cx), [4..4, 6..6]);
8363
8364 editor.transpose(&Default::default(), cx);
8365 assert_eq!(editor.text(cx), "bcaed\n");
8366 assert_eq!(editor.selected_ranges(cx), [5..5, 6..6]);
8367
8368 editor
8369 })
8370 .1;
8371
8372 cx.add_window(Default::default(), |cx| {
8373 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8374
8375 editor.select_ranges([4..4], None, cx);
8376 editor.transpose(&Default::default(), cx);
8377 assert_eq!(editor.text(cx), "🏀🍐✋");
8378 assert_eq!(editor.selected_ranges(cx), [8..8]);
8379
8380 editor.transpose(&Default::default(), cx);
8381 assert_eq!(editor.text(cx), "🏀✋🍐");
8382 assert_eq!(editor.selected_ranges(cx), [11..11]);
8383
8384 editor.transpose(&Default::default(), cx);
8385 assert_eq!(editor.text(cx), "🏀🍐✋");
8386 assert_eq!(editor.selected_ranges(cx), [11..11]);
8387
8388 editor
8389 })
8390 .1;
8391 }
8392
8393 #[gpui::test]
8394 fn test_clipboard(cx: &mut gpui::MutableAppContext) {
8395 cx.set_global(Settings::test(cx));
8396 let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
8397 let view = cx
8398 .add_window(Default::default(), |cx| build_editor(buffer.clone(), cx))
8399 .1;
8400
8401 // Cut with three selections. Clipboard text is divided into three slices.
8402 view.update(cx, |view, cx| {
8403 view.select_ranges(vec![0..7, 11..17, 22..27], None, cx);
8404 view.cut(&Cut, cx);
8405 assert_eq!(view.display_text(cx), "two four six ");
8406 });
8407
8408 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8409 view.update(cx, |view, cx| {
8410 view.select_ranges(vec![4..4, 9..9, 13..13], None, cx);
8411 view.paste(&Paste, cx);
8412 assert_eq!(view.display_text(cx), "two one✅ four three six five ");
8413 assert_eq!(
8414 view.selected_display_ranges(cx),
8415 &[
8416 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
8417 DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
8418 DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
8419 ]
8420 );
8421 });
8422
8423 // Paste again but with only two cursors. Since the number of cursors doesn't
8424 // match the number of slices in the clipboard, the entire clipboard text
8425 // is pasted at each cursor.
8426 view.update(cx, |view, cx| {
8427 view.select_ranges(vec![0..0, 31..31], None, cx);
8428 view.handle_input(&Input("( ".into()), cx);
8429 view.paste(&Paste, cx);
8430 view.handle_input(&Input(") ".into()), cx);
8431 assert_eq!(
8432 view.display_text(cx),
8433 "( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8434 );
8435 });
8436
8437 view.update(cx, |view, cx| {
8438 view.select_ranges(vec![0..0], None, cx);
8439 view.handle_input(&Input("123\n4567\n89\n".into()), cx);
8440 assert_eq!(
8441 view.display_text(cx),
8442 "123\n4567\n89\n( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8443 );
8444 });
8445
8446 // Cut with three selections, one of which is full-line.
8447 view.update(cx, |view, cx| {
8448 view.select_display_ranges(
8449 &[
8450 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
8451 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8452 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
8453 ],
8454 cx,
8455 );
8456 view.cut(&Cut, cx);
8457 assert_eq!(
8458 view.display_text(cx),
8459 "13\n9\n( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8460 );
8461 });
8462
8463 // Paste with three selections, noticing how the copied selection that was full-line
8464 // gets inserted before the second cursor.
8465 view.update(cx, |view, cx| {
8466 view.select_display_ranges(
8467 &[
8468 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8469 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8470 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
8471 ],
8472 cx,
8473 );
8474 view.paste(&Paste, cx);
8475 assert_eq!(
8476 view.display_text(cx),
8477 "123\n4567\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8478 );
8479 assert_eq!(
8480 view.selected_display_ranges(cx),
8481 &[
8482 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8483 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8484 DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
8485 ]
8486 );
8487 });
8488
8489 // Copy with a single cursor only, which writes the whole line into the clipboard.
8490 view.update(cx, |view, cx| {
8491 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], cx);
8492 view.copy(&Copy, cx);
8493 });
8494
8495 // Paste with three selections, noticing how the copied full-line selection is inserted
8496 // before the empty selections but replaces the selection that is non-empty.
8497 view.update(cx, |view, cx| {
8498 view.select_display_ranges(
8499 &[
8500 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8501 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
8502 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8503 ],
8504 cx,
8505 );
8506 view.paste(&Paste, cx);
8507 assert_eq!(
8508 view.display_text(cx),
8509 "123\n123\n123\n67\n123\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8510 );
8511 assert_eq!(
8512 view.selected_display_ranges(cx),
8513 &[
8514 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8515 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8516 DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
8517 ]
8518 );
8519 });
8520 }
8521
8522 #[gpui::test]
8523 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8524 cx.set_global(Settings::test(cx));
8525 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8526 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8527 view.update(cx, |view, cx| {
8528 view.select_all(&SelectAll, cx);
8529 assert_eq!(
8530 view.selected_display_ranges(cx),
8531 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8532 );
8533 });
8534 }
8535
8536 #[gpui::test]
8537 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8538 cx.set_global(Settings::test(cx));
8539 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8540 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8541 view.update(cx, |view, cx| {
8542 view.select_display_ranges(
8543 &[
8544 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8545 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8546 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8547 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8548 ],
8549 cx,
8550 );
8551 view.select_line(&SelectLine, cx);
8552 assert_eq!(
8553 view.selected_display_ranges(cx),
8554 vec![
8555 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8556 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8557 ]
8558 );
8559 });
8560
8561 view.update(cx, |view, cx| {
8562 view.select_line(&SelectLine, cx);
8563 assert_eq!(
8564 view.selected_display_ranges(cx),
8565 vec![
8566 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8567 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8568 ]
8569 );
8570 });
8571
8572 view.update(cx, |view, cx| {
8573 view.select_line(&SelectLine, cx);
8574 assert_eq!(
8575 view.selected_display_ranges(cx),
8576 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8577 );
8578 });
8579 }
8580
8581 #[gpui::test]
8582 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8583 cx.set_global(Settings::test(cx));
8584 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8585 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8586 view.update(cx, |view, cx| {
8587 view.fold_ranges(
8588 vec![
8589 Point::new(0, 2)..Point::new(1, 2),
8590 Point::new(2, 3)..Point::new(4, 1),
8591 Point::new(7, 0)..Point::new(8, 4),
8592 ],
8593 cx,
8594 );
8595 view.select_display_ranges(
8596 &[
8597 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8598 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8599 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8600 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8601 ],
8602 cx,
8603 );
8604 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8605 });
8606
8607 view.update(cx, |view, cx| {
8608 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8609 assert_eq!(
8610 view.display_text(cx),
8611 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8612 );
8613 assert_eq!(
8614 view.selected_display_ranges(cx),
8615 [
8616 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8617 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8618 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8619 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8620 ]
8621 );
8622 });
8623
8624 view.update(cx, |view, cx| {
8625 view.select_display_ranges(&[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)], cx);
8626 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8627 assert_eq!(
8628 view.display_text(cx),
8629 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8630 );
8631 assert_eq!(
8632 view.selected_display_ranges(cx),
8633 [
8634 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8635 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8636 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8637 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8638 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8639 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8640 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8641 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8642 ]
8643 );
8644 });
8645 }
8646
8647 #[gpui::test]
8648 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8649 cx.set_global(Settings::test(cx));
8650 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8651 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8652
8653 view.update(cx, |view, cx| {
8654 view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx);
8655 });
8656 view.update(cx, |view, cx| {
8657 view.add_selection_above(&AddSelectionAbove, cx);
8658 assert_eq!(
8659 view.selected_display_ranges(cx),
8660 vec![
8661 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8662 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8663 ]
8664 );
8665 });
8666
8667 view.update(cx, |view, cx| {
8668 view.add_selection_above(&AddSelectionAbove, cx);
8669 assert_eq!(
8670 view.selected_display_ranges(cx),
8671 vec![
8672 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8673 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8674 ]
8675 );
8676 });
8677
8678 view.update(cx, |view, cx| {
8679 view.add_selection_below(&AddSelectionBelow, cx);
8680 assert_eq!(
8681 view.selected_display_ranges(cx),
8682 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8683 );
8684
8685 view.undo_selection(&UndoSelection, cx);
8686 assert_eq!(
8687 view.selected_display_ranges(cx),
8688 vec![
8689 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8690 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8691 ]
8692 );
8693
8694 view.redo_selection(&RedoSelection, cx);
8695 assert_eq!(
8696 view.selected_display_ranges(cx),
8697 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8698 );
8699 });
8700
8701 view.update(cx, |view, cx| {
8702 view.add_selection_below(&AddSelectionBelow, cx);
8703 assert_eq!(
8704 view.selected_display_ranges(cx),
8705 vec![
8706 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8707 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8708 ]
8709 );
8710 });
8711
8712 view.update(cx, |view, cx| {
8713 view.add_selection_below(&AddSelectionBelow, cx);
8714 assert_eq!(
8715 view.selected_display_ranges(cx),
8716 vec![
8717 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8718 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8719 ]
8720 );
8721 });
8722
8723 view.update(cx, |view, cx| {
8724 view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], cx);
8725 });
8726 view.update(cx, |view, cx| {
8727 view.add_selection_below(&AddSelectionBelow, cx);
8728 assert_eq!(
8729 view.selected_display_ranges(cx),
8730 vec![
8731 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8732 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8733 ]
8734 );
8735 });
8736
8737 view.update(cx, |view, cx| {
8738 view.add_selection_below(&AddSelectionBelow, cx);
8739 assert_eq!(
8740 view.selected_display_ranges(cx),
8741 vec![
8742 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8743 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8744 ]
8745 );
8746 });
8747
8748 view.update(cx, |view, cx| {
8749 view.add_selection_above(&AddSelectionAbove, cx);
8750 assert_eq!(
8751 view.selected_display_ranges(cx),
8752 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8753 );
8754 });
8755
8756 view.update(cx, |view, cx| {
8757 view.add_selection_above(&AddSelectionAbove, cx);
8758 assert_eq!(
8759 view.selected_display_ranges(cx),
8760 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8761 );
8762 });
8763
8764 view.update(cx, |view, cx| {
8765 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], cx);
8766 view.add_selection_below(&AddSelectionBelow, cx);
8767 assert_eq!(
8768 view.selected_display_ranges(cx),
8769 vec![
8770 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8771 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8772 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8773 ]
8774 );
8775 });
8776
8777 view.update(cx, |view, cx| {
8778 view.add_selection_below(&AddSelectionBelow, cx);
8779 assert_eq!(
8780 view.selected_display_ranges(cx),
8781 vec![
8782 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8783 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8784 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8785 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8786 ]
8787 );
8788 });
8789
8790 view.update(cx, |view, cx| {
8791 view.add_selection_above(&AddSelectionAbove, cx);
8792 assert_eq!(
8793 view.selected_display_ranges(cx),
8794 vec![
8795 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8796 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8797 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8798 ]
8799 );
8800 });
8801
8802 view.update(cx, |view, cx| {
8803 view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], cx);
8804 });
8805 view.update(cx, |view, cx| {
8806 view.add_selection_above(&AddSelectionAbove, cx);
8807 assert_eq!(
8808 view.selected_display_ranges(cx),
8809 vec![
8810 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8811 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8812 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8813 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8814 ]
8815 );
8816 });
8817
8818 view.update(cx, |view, cx| {
8819 view.add_selection_below(&AddSelectionBelow, cx);
8820 assert_eq!(
8821 view.selected_display_ranges(cx),
8822 vec![
8823 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8824 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8825 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8826 ]
8827 );
8828 });
8829 }
8830
8831 #[gpui::test]
8832 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8833 cx.set_global(Settings::test(cx));
8834
8835 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8836 let buffer = MultiBuffer::build_simple(&text, cx);
8837 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8838
8839 view.update(cx, |view, cx| {
8840 view.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None, cx);
8841 view.select_next(
8842 &SelectNext {
8843 replace_newest: false,
8844 },
8845 cx,
8846 );
8847 assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
8848
8849 view.select_next(
8850 &SelectNext {
8851 replace_newest: false,
8852 },
8853 cx,
8854 );
8855 assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
8856
8857 view.undo_selection(&UndoSelection, cx);
8858 assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
8859
8860 view.redo_selection(&RedoSelection, cx);
8861 assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
8862
8863 view.select_next(
8864 &SelectNext {
8865 replace_newest: false,
8866 },
8867 cx,
8868 );
8869 assert_eq!(view.selected_ranges(cx), &ranges[1..4]);
8870
8871 view.select_next(
8872 &SelectNext {
8873 replace_newest: false,
8874 },
8875 cx,
8876 );
8877 assert_eq!(view.selected_ranges(cx), &ranges[0..4]);
8878 });
8879 }
8880
8881 #[gpui::test]
8882 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8883 cx.update(|cx| cx.set_global(Settings::test(cx)));
8884 let language = Arc::new(Language::new(
8885 LanguageConfig::default(),
8886 Some(tree_sitter_rust::language()),
8887 ));
8888
8889 let text = r#"
8890 use mod1::mod2::{mod3, mod4};
8891
8892 fn fn_1(param1: bool, param2: &str) {
8893 let var1 = "text";
8894 }
8895 "#
8896 .unindent();
8897
8898 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8899 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8900 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8901 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8902 .await;
8903
8904 view.update(cx, |view, cx| {
8905 view.select_display_ranges(
8906 &[
8907 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8908 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8909 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8910 ],
8911 cx,
8912 );
8913 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8914 });
8915 assert_eq!(
8916 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8917 &[
8918 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8919 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8920 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8921 ]
8922 );
8923
8924 view.update(cx, |view, cx| {
8925 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8926 });
8927 assert_eq!(
8928 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8929 &[
8930 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8931 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8932 ]
8933 );
8934
8935 view.update(cx, |view, cx| {
8936 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8937 });
8938 assert_eq!(
8939 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8940 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8941 );
8942
8943 // Trying to expand the selected syntax node one more time has no effect.
8944 view.update(cx, |view, cx| {
8945 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8946 });
8947 assert_eq!(
8948 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8949 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8950 );
8951
8952 view.update(cx, |view, cx| {
8953 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8954 });
8955 assert_eq!(
8956 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8957 &[
8958 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8959 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8960 ]
8961 );
8962
8963 view.update(cx, |view, cx| {
8964 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8965 });
8966 assert_eq!(
8967 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8968 &[
8969 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8970 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8971 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8972 ]
8973 );
8974
8975 view.update(cx, |view, cx| {
8976 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8977 });
8978 assert_eq!(
8979 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8980 &[
8981 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8982 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8983 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8984 ]
8985 );
8986
8987 // Trying to shrink the selected syntax node one more time has no effect.
8988 view.update(cx, |view, cx| {
8989 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8990 });
8991 assert_eq!(
8992 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8993 &[
8994 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8995 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8996 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8997 ]
8998 );
8999
9000 // Ensure that we keep expanding the selection if the larger selection starts or ends within
9001 // a fold.
9002 view.update(cx, |view, cx| {
9003 view.fold_ranges(
9004 vec![
9005 Point::new(0, 21)..Point::new(0, 24),
9006 Point::new(3, 20)..Point::new(3, 22),
9007 ],
9008 cx,
9009 );
9010 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
9011 });
9012 assert_eq!(
9013 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
9014 &[
9015 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
9016 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
9017 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
9018 ]
9019 );
9020 }
9021
9022 #[gpui::test]
9023 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
9024 cx.update(|cx| cx.set_global(Settings::test(cx)));
9025 let language = Arc::new(
9026 Language::new(
9027 LanguageConfig {
9028 brackets: vec![
9029 BracketPair {
9030 start: "{".to_string(),
9031 end: "}".to_string(),
9032 close: false,
9033 newline: true,
9034 },
9035 BracketPair {
9036 start: "(".to_string(),
9037 end: ")".to_string(),
9038 close: false,
9039 newline: true,
9040 },
9041 ],
9042 ..Default::default()
9043 },
9044 Some(tree_sitter_rust::language()),
9045 )
9046 .with_indents_query(
9047 r#"
9048 (_ "(" ")" @end) @indent
9049 (_ "{" "}" @end) @indent
9050 "#,
9051 )
9052 .unwrap(),
9053 );
9054
9055 let text = "fn a() {}";
9056
9057 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9058 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9059 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9060 editor
9061 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
9062 .await;
9063
9064 editor.update(cx, |editor, cx| {
9065 editor.select_ranges([5..5, 8..8, 9..9], None, cx);
9066 editor.newline(&Newline, cx);
9067 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
9068 assert_eq!(
9069 editor.selected_ranges(cx),
9070 &[
9071 Point::new(1, 4)..Point::new(1, 4),
9072 Point::new(3, 4)..Point::new(3, 4),
9073 Point::new(5, 0)..Point::new(5, 0)
9074 ]
9075 );
9076 });
9077 }
9078
9079 #[gpui::test]
9080 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
9081 cx.update(|cx| cx.set_global(Settings::test(cx)));
9082 let language = Arc::new(Language::new(
9083 LanguageConfig {
9084 brackets: vec![
9085 BracketPair {
9086 start: "{".to_string(),
9087 end: "}".to_string(),
9088 close: true,
9089 newline: true,
9090 },
9091 BracketPair {
9092 start: "/*".to_string(),
9093 end: " */".to_string(),
9094 close: true,
9095 newline: true,
9096 },
9097 ],
9098 autoclose_before: "})]".to_string(),
9099 ..Default::default()
9100 },
9101 Some(tree_sitter_rust::language()),
9102 ));
9103
9104 let text = r#"
9105 a
9106
9107 /
9108
9109 "#
9110 .unindent();
9111
9112 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9113 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9114 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9115 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9116 .await;
9117
9118 view.update(cx, |view, cx| {
9119 view.select_display_ranges(
9120 &[
9121 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9122 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
9123 ],
9124 cx,
9125 );
9126
9127 view.handle_input(&Input("{".to_string()), cx);
9128 view.handle_input(&Input("{".to_string()), cx);
9129 view.handle_input(&Input("{".to_string()), cx);
9130 assert_eq!(
9131 view.text(cx),
9132 "
9133 {{{}}}
9134 {{{}}}
9135 /
9136
9137 "
9138 .unindent()
9139 );
9140
9141 view.move_right(&MoveRight, cx);
9142 view.handle_input(&Input("}".to_string()), cx);
9143 view.handle_input(&Input("}".to_string()), cx);
9144 view.handle_input(&Input("}".to_string()), cx);
9145 assert_eq!(
9146 view.text(cx),
9147 "
9148 {{{}}}}
9149 {{{}}}}
9150 /
9151
9152 "
9153 .unindent()
9154 );
9155
9156 view.undo(&Undo, cx);
9157 view.handle_input(&Input("/".to_string()), cx);
9158 view.handle_input(&Input("*".to_string()), cx);
9159 assert_eq!(
9160 view.text(cx),
9161 "
9162 /* */
9163 /* */
9164 /
9165
9166 "
9167 .unindent()
9168 );
9169
9170 view.undo(&Undo, cx);
9171 view.select_display_ranges(
9172 &[
9173 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
9174 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
9175 ],
9176 cx,
9177 );
9178 view.handle_input(&Input("*".to_string()), cx);
9179 assert_eq!(
9180 view.text(cx),
9181 "
9182 a
9183
9184 /*
9185 *
9186 "
9187 .unindent()
9188 );
9189
9190 // Don't autoclose if the next character isn't whitespace and isn't
9191 // listed in the language's "autoclose_before" section.
9192 view.finalize_last_transaction(cx);
9193 view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)], cx);
9194 view.handle_input(&Input("{".to_string()), cx);
9195 assert_eq!(
9196 view.text(cx),
9197 "
9198 {a
9199
9200 /*
9201 *
9202 "
9203 .unindent()
9204 );
9205
9206 view.undo(&Undo, cx);
9207 view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)], cx);
9208 view.handle_input(&Input("{".to_string()), cx);
9209 assert_eq!(
9210 view.text(cx),
9211 "
9212 {a}
9213
9214 /*
9215 *
9216 "
9217 .unindent()
9218 );
9219 assert_eq!(
9220 view.selected_display_ranges(cx),
9221 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
9222 );
9223 });
9224 }
9225
9226 #[gpui::test]
9227 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9228 cx.update(|cx| cx.set_global(Settings::test(cx)));
9229
9230 let text = "
9231 a. b
9232 a. b
9233 a. b
9234 "
9235 .unindent();
9236 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9237 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9238
9239 editor.update(cx, |editor, cx| {
9240 let buffer = &editor.snapshot(cx).buffer_snapshot;
9241 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9242 let insertion_ranges = [
9243 Point::new(0, 2).to_offset(buffer)..Point::new(0, 2).to_offset(buffer),
9244 Point::new(1, 2).to_offset(buffer)..Point::new(1, 2).to_offset(buffer),
9245 Point::new(2, 2).to_offset(buffer)..Point::new(2, 2).to_offset(buffer),
9246 ];
9247
9248 editor
9249 .insert_snippet(&insertion_ranges, snippet, cx)
9250 .unwrap();
9251 assert_eq!(
9252 editor.text(cx),
9253 "
9254 a.f(one, two, three) b
9255 a.f(one, two, three) b
9256 a.f(one, two, three) b
9257 "
9258 .unindent()
9259 );
9260 assert_eq!(
9261 editor.selected_ranges::<Point>(cx),
9262 &[
9263 Point::new(0, 4)..Point::new(0, 7),
9264 Point::new(0, 14)..Point::new(0, 19),
9265 Point::new(1, 4)..Point::new(1, 7),
9266 Point::new(1, 14)..Point::new(1, 19),
9267 Point::new(2, 4)..Point::new(2, 7),
9268 Point::new(2, 14)..Point::new(2, 19),
9269 ]
9270 );
9271
9272 // Can't move earlier than the first tab stop
9273 editor.move_to_prev_snippet_tabstop(cx);
9274 assert_eq!(
9275 editor.selected_ranges::<Point>(cx),
9276 &[
9277 Point::new(0, 4)..Point::new(0, 7),
9278 Point::new(0, 14)..Point::new(0, 19),
9279 Point::new(1, 4)..Point::new(1, 7),
9280 Point::new(1, 14)..Point::new(1, 19),
9281 Point::new(2, 4)..Point::new(2, 7),
9282 Point::new(2, 14)..Point::new(2, 19),
9283 ]
9284 );
9285
9286 assert!(editor.move_to_next_snippet_tabstop(cx));
9287 assert_eq!(
9288 editor.selected_ranges::<Point>(cx),
9289 &[
9290 Point::new(0, 9)..Point::new(0, 12),
9291 Point::new(1, 9)..Point::new(1, 12),
9292 Point::new(2, 9)..Point::new(2, 12)
9293 ]
9294 );
9295
9296 editor.move_to_prev_snippet_tabstop(cx);
9297 assert_eq!(
9298 editor.selected_ranges::<Point>(cx),
9299 &[
9300 Point::new(0, 4)..Point::new(0, 7),
9301 Point::new(0, 14)..Point::new(0, 19),
9302 Point::new(1, 4)..Point::new(1, 7),
9303 Point::new(1, 14)..Point::new(1, 19),
9304 Point::new(2, 4)..Point::new(2, 7),
9305 Point::new(2, 14)..Point::new(2, 19),
9306 ]
9307 );
9308
9309 assert!(editor.move_to_next_snippet_tabstop(cx));
9310 assert!(editor.move_to_next_snippet_tabstop(cx));
9311 assert_eq!(
9312 editor.selected_ranges::<Point>(cx),
9313 &[
9314 Point::new(0, 20)..Point::new(0, 20),
9315 Point::new(1, 20)..Point::new(1, 20),
9316 Point::new(2, 20)..Point::new(2, 20)
9317 ]
9318 );
9319
9320 // As soon as the last tab stop is reached, snippet state is gone
9321 editor.move_to_prev_snippet_tabstop(cx);
9322 assert_eq!(
9323 editor.selected_ranges::<Point>(cx),
9324 &[
9325 Point::new(0, 20)..Point::new(0, 20),
9326 Point::new(1, 20)..Point::new(1, 20),
9327 Point::new(2, 20)..Point::new(2, 20)
9328 ]
9329 );
9330 });
9331 }
9332
9333 #[gpui::test]
9334 async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
9335 cx.foreground().forbid_parking();
9336 cx.update(|cx| cx.set_global(Settings::test(cx)));
9337
9338 let mut language = Language::new(
9339 LanguageConfig {
9340 name: "Rust".into(),
9341 path_suffixes: vec!["rs".to_string()],
9342 ..Default::default()
9343 },
9344 Some(tree_sitter_rust::language()),
9345 );
9346 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9347 capabilities: lsp::ServerCapabilities {
9348 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9349 ..Default::default()
9350 },
9351 ..Default::default()
9352 });
9353
9354 let fs = FakeFs::new(cx.background().clone());
9355 fs.insert_file("/file.rs", Default::default()).await;
9356
9357 let project = Project::test(fs, ["/file.rs"], cx).await;
9358 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9359 let buffer = project
9360 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9361 .await
9362 .unwrap();
9363
9364 cx.foreground().start_waiting();
9365 let fake_server = fake_servers.next().await.unwrap();
9366
9367 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9368 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9369 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9370 assert!(cx.read(|cx| editor.is_dirty(cx)));
9371
9372 let save = cx.update(|cx| editor.save(project.clone(), cx));
9373 fake_server
9374 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9375 assert_eq!(
9376 params.text_document.uri,
9377 lsp::Url::from_file_path("/file.rs").unwrap()
9378 );
9379 assert_eq!(params.options.tab_size, 4);
9380 Ok(Some(vec![lsp::TextEdit::new(
9381 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9382 ", ".to_string(),
9383 )]))
9384 })
9385 .next()
9386 .await;
9387 cx.foreground().start_waiting();
9388 save.await.unwrap();
9389 assert_eq!(
9390 editor.read_with(cx, |editor, cx| editor.text(cx)),
9391 "one, two\nthree\n"
9392 );
9393 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9394
9395 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9396 assert!(cx.read(|cx| editor.is_dirty(cx)));
9397
9398 // Ensure we can still save even if formatting hangs.
9399 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9400 assert_eq!(
9401 params.text_document.uri,
9402 lsp::Url::from_file_path("/file.rs").unwrap()
9403 );
9404 futures::future::pending::<()>().await;
9405 unreachable!()
9406 });
9407 let save = cx.update(|cx| editor.save(project.clone(), cx));
9408 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9409 cx.foreground().start_waiting();
9410 save.await.unwrap();
9411 assert_eq!(
9412 editor.read_with(cx, |editor, cx| editor.text(cx)),
9413 "one\ntwo\nthree\n"
9414 );
9415 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9416
9417 // Set rust language override and assert overriden tabsize is sent to language server
9418 cx.update(|cx| {
9419 cx.update_global::<Settings, _, _>(|settings, _| {
9420 settings.language_overrides.insert(
9421 "Rust".into(),
9422 LanguageOverride {
9423 tab_size: Some(8),
9424 ..Default::default()
9425 },
9426 );
9427 })
9428 });
9429
9430 let save = cx.update(|cx| editor.save(project.clone(), cx));
9431 fake_server
9432 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9433 assert_eq!(
9434 params.text_document.uri,
9435 lsp::Url::from_file_path("/file.rs").unwrap()
9436 );
9437 assert_eq!(params.options.tab_size, 8);
9438 Ok(Some(vec![]))
9439 })
9440 .next()
9441 .await;
9442 cx.foreground().start_waiting();
9443 save.await.unwrap();
9444 }
9445
9446 #[gpui::test]
9447 async fn test_completion(cx: &mut gpui::TestAppContext) {
9448 cx.update(|cx| cx.set_global(Settings::test(cx)));
9449
9450 let mut language = Language::new(
9451 LanguageConfig {
9452 name: "Rust".into(),
9453 path_suffixes: vec!["rs".to_string()],
9454 ..Default::default()
9455 },
9456 Some(tree_sitter_rust::language()),
9457 );
9458 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9459 capabilities: lsp::ServerCapabilities {
9460 completion_provider: Some(lsp::CompletionOptions {
9461 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9462 ..Default::default()
9463 }),
9464 ..Default::default()
9465 },
9466 ..Default::default()
9467 });
9468
9469 let text = "
9470 one
9471 two
9472 three
9473 "
9474 .unindent();
9475
9476 let fs = FakeFs::new(cx.background().clone());
9477 fs.insert_file("/file.rs", text).await;
9478
9479 let project = Project::test(fs, ["/file.rs"], cx).await;
9480 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9481 let buffer = project
9482 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9483 .await
9484 .unwrap();
9485 let mut fake_server = fake_servers.next().await.unwrap();
9486
9487 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9488 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9489
9490 editor.update(cx, |editor, cx| {
9491 editor.project = Some(project);
9492 editor.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None, cx);
9493 editor.handle_input(&Input(".".to_string()), cx);
9494 });
9495
9496 handle_completion_request(
9497 &mut fake_server,
9498 "/file.rs",
9499 Point::new(0, 4),
9500 vec![
9501 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9502 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9503 ],
9504 )
9505 .await;
9506 editor
9507 .condition(&cx, |editor, _| editor.context_menu_visible())
9508 .await;
9509
9510 let apply_additional_edits = editor.update(cx, |editor, cx| {
9511 editor.move_down(&MoveDown, cx);
9512 let apply_additional_edits = editor
9513 .confirm_completion(&ConfirmCompletion::default(), cx)
9514 .unwrap();
9515 assert_eq!(
9516 editor.text(cx),
9517 "
9518 one.second_completion
9519 two
9520 three
9521 "
9522 .unindent()
9523 );
9524 apply_additional_edits
9525 });
9526
9527 handle_resolve_completion_request(
9528 &mut fake_server,
9529 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9530 )
9531 .await;
9532 apply_additional_edits.await.unwrap();
9533 assert_eq!(
9534 editor.read_with(cx, |editor, cx| editor.text(cx)),
9535 "
9536 one.second_completion
9537 two
9538 three
9539 additional edit
9540 "
9541 .unindent()
9542 );
9543
9544 editor.update(cx, |editor, cx| {
9545 editor.select_ranges(
9546 [
9547 Point::new(1, 3)..Point::new(1, 3),
9548 Point::new(2, 5)..Point::new(2, 5),
9549 ],
9550 None,
9551 cx,
9552 );
9553
9554 editor.handle_input(&Input(" ".to_string()), cx);
9555 assert!(editor.context_menu.is_none());
9556 editor.handle_input(&Input("s".to_string()), cx);
9557 assert!(editor.context_menu.is_none());
9558 });
9559
9560 handle_completion_request(
9561 &mut fake_server,
9562 "/file.rs",
9563 Point::new(2, 7),
9564 vec![
9565 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9566 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9567 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9568 ],
9569 )
9570 .await;
9571 editor
9572 .condition(&cx, |editor, _| editor.context_menu_visible())
9573 .await;
9574
9575 editor.update(cx, |editor, cx| {
9576 editor.handle_input(&Input("i".to_string()), cx);
9577 });
9578
9579 handle_completion_request(
9580 &mut fake_server,
9581 "/file.rs",
9582 Point::new(2, 8),
9583 vec![
9584 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9585 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9586 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9587 ],
9588 )
9589 .await;
9590 editor
9591 .condition(&cx, |editor, _| editor.context_menu_visible())
9592 .await;
9593
9594 let apply_additional_edits = editor.update(cx, |editor, cx| {
9595 let apply_additional_edits = editor
9596 .confirm_completion(&ConfirmCompletion::default(), cx)
9597 .unwrap();
9598 assert_eq!(
9599 editor.text(cx),
9600 "
9601 one.second_completion
9602 two sixth_completion
9603 three sixth_completion
9604 additional edit
9605 "
9606 .unindent()
9607 );
9608 apply_additional_edits
9609 });
9610 handle_resolve_completion_request(&mut fake_server, None).await;
9611 apply_additional_edits.await.unwrap();
9612
9613 async fn handle_completion_request(
9614 fake: &mut FakeLanguageServer,
9615 path: &'static str,
9616 position: Point,
9617 completions: Vec<(Range<Point>, &'static str)>,
9618 ) {
9619 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9620 let completions = completions.clone();
9621 async move {
9622 assert_eq!(
9623 params.text_document_position.text_document.uri,
9624 lsp::Url::from_file_path(path).unwrap()
9625 );
9626 assert_eq!(
9627 params.text_document_position.position,
9628 lsp::Position::new(position.row, position.column)
9629 );
9630 Ok(Some(lsp::CompletionResponse::Array(
9631 completions
9632 .iter()
9633 .map(|(range, new_text)| lsp::CompletionItem {
9634 label: new_text.to_string(),
9635 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9636 range: lsp::Range::new(
9637 lsp::Position::new(range.start.row, range.start.column),
9638 lsp::Position::new(range.start.row, range.start.column),
9639 ),
9640 new_text: new_text.to_string(),
9641 })),
9642 ..Default::default()
9643 })
9644 .collect(),
9645 )))
9646 }
9647 })
9648 .next()
9649 .await;
9650 }
9651
9652 async fn handle_resolve_completion_request(
9653 fake: &mut FakeLanguageServer,
9654 edit: Option<(Range<Point>, &'static str)>,
9655 ) {
9656 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9657 let edit = edit.clone();
9658 async move {
9659 Ok(lsp::CompletionItem {
9660 additional_text_edits: edit.map(|(range, new_text)| {
9661 vec![lsp::TextEdit::new(
9662 lsp::Range::new(
9663 lsp::Position::new(range.start.row, range.start.column),
9664 lsp::Position::new(range.end.row, range.end.column),
9665 ),
9666 new_text.to_string(),
9667 )]
9668 }),
9669 ..Default::default()
9670 })
9671 }
9672 })
9673 .next()
9674 .await;
9675 }
9676 }
9677
9678 #[gpui::test]
9679 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9680 cx.update(|cx| cx.set_global(Settings::test(cx)));
9681 let language = Arc::new(Language::new(
9682 LanguageConfig {
9683 line_comment: Some("// ".to_string()),
9684 ..Default::default()
9685 },
9686 Some(tree_sitter_rust::language()),
9687 ));
9688
9689 let text = "
9690 fn a() {
9691 //b();
9692 // c();
9693 // d();
9694 }
9695 "
9696 .unindent();
9697
9698 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9699 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9700 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9701
9702 view.update(cx, |editor, cx| {
9703 // If multiple selections intersect a line, the line is only
9704 // toggled once.
9705 editor.select_display_ranges(
9706 &[
9707 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9708 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9709 ],
9710 cx,
9711 );
9712 editor.toggle_comments(&ToggleComments, cx);
9713 assert_eq!(
9714 editor.text(cx),
9715 "
9716 fn a() {
9717 b();
9718 c();
9719 d();
9720 }
9721 "
9722 .unindent()
9723 );
9724
9725 // The comment prefix is inserted at the same column for every line
9726 // in a selection.
9727 editor.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)], cx);
9728 editor.toggle_comments(&ToggleComments, cx);
9729 assert_eq!(
9730 editor.text(cx),
9731 "
9732 fn a() {
9733 // b();
9734 // c();
9735 // d();
9736 }
9737 "
9738 .unindent()
9739 );
9740
9741 // If a selection ends at the beginning of a line, that line is not toggled.
9742 editor.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)], cx);
9743 editor.toggle_comments(&ToggleComments, cx);
9744 assert_eq!(
9745 editor.text(cx),
9746 "
9747 fn a() {
9748 // b();
9749 c();
9750 // d();
9751 }
9752 "
9753 .unindent()
9754 );
9755 });
9756 }
9757
9758 #[gpui::test]
9759 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9760 cx.set_global(Settings::test(cx));
9761 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9762 let multibuffer = cx.add_model(|cx| {
9763 let mut multibuffer = MultiBuffer::new(0);
9764 multibuffer.push_excerpts(
9765 buffer.clone(),
9766 [
9767 Point::new(0, 0)..Point::new(0, 4),
9768 Point::new(1, 0)..Point::new(1, 4),
9769 ],
9770 cx,
9771 );
9772 multibuffer
9773 });
9774
9775 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9776
9777 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9778 view.update(cx, |view, cx| {
9779 assert_eq!(view.text(cx), "aaaa\nbbbb");
9780 view.select_ranges(
9781 [
9782 Point::new(0, 0)..Point::new(0, 0),
9783 Point::new(1, 0)..Point::new(1, 0),
9784 ],
9785 None,
9786 cx,
9787 );
9788
9789 view.handle_input(&Input("X".to_string()), cx);
9790 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9791 assert_eq!(
9792 view.selected_ranges(cx),
9793 [
9794 Point::new(0, 1)..Point::new(0, 1),
9795 Point::new(1, 1)..Point::new(1, 1),
9796 ]
9797 )
9798 });
9799 }
9800
9801 #[gpui::test]
9802 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9803 cx.set_global(Settings::test(cx));
9804 let buffer = cx.add_model(|cx| {
9805 Buffer::new(
9806 0,
9807 indoc! {"
9808 aaaa
9809 bbbb
9810 cccc"},
9811 cx,
9812 )
9813 });
9814
9815 let multibuffer = cx.add_model(|cx| {
9816 let mut multibuffer = MultiBuffer::new(0);
9817 multibuffer.push_excerpts(
9818 buffer,
9819 [
9820 Point::new(0, 0)..Point::new(1, 4),
9821 Point::new(1, 0)..Point::new(2, 4),
9822 ],
9823 cx,
9824 );
9825 multibuffer
9826 });
9827
9828 assert_eq!(
9829 multibuffer.read(cx).read(cx).text(),
9830 "aaaa\nbbbb\nbbbb\ncccc"
9831 );
9832
9833 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9834 view.update(cx, |view, cx| {
9835 view.select_ranges(
9836 [
9837 Point::new(1, 1)..Point::new(1, 1),
9838 Point::new(2, 3)..Point::new(2, 3),
9839 ],
9840 None,
9841 cx,
9842 );
9843
9844 view.handle_input(&Input("X".to_string()), cx);
9845 assert_eq!(view.text(cx), "aaaa\nbXbbXb\nbXbbXb\ncccc");
9846 assert_eq!(
9847 view.selected_ranges(cx),
9848 [
9849 Point::new(1, 2)..Point::new(1, 2),
9850 Point::new(2, 5)..Point::new(2, 5),
9851 ]
9852 );
9853
9854 view.newline(&Newline, cx);
9855 assert_eq!(view.text(cx), "aaaa\nbX\nbbX\nb\nbX\nbbX\nb\ncccc");
9856 assert_eq!(
9857 view.selected_ranges(cx),
9858 [
9859 Point::new(2, 0)..Point::new(2, 0),
9860 Point::new(6, 0)..Point::new(6, 0),
9861 ]
9862 );
9863 });
9864 }
9865
9866 #[gpui::test]
9867 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9868 cx.set_global(Settings::test(cx));
9869 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9870 let mut excerpt1_id = None;
9871 let multibuffer = cx.add_model(|cx| {
9872 let mut multibuffer = MultiBuffer::new(0);
9873 excerpt1_id = multibuffer
9874 .push_excerpts(
9875 buffer.clone(),
9876 [
9877 Point::new(0, 0)..Point::new(1, 4),
9878 Point::new(1, 0)..Point::new(2, 4),
9879 ],
9880 cx,
9881 )
9882 .into_iter()
9883 .next();
9884 multibuffer
9885 });
9886 assert_eq!(
9887 multibuffer.read(cx).read(cx).text(),
9888 "aaaa\nbbbb\nbbbb\ncccc"
9889 );
9890 let (_, editor) = cx.add_window(Default::default(), |cx| {
9891 let mut editor = build_editor(multibuffer.clone(), cx);
9892 let snapshot = editor.snapshot(cx);
9893 editor.select_ranges([Point::new(1, 3)..Point::new(1, 3)], None, cx);
9894 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9895 assert_eq!(
9896 editor.selected_ranges(cx),
9897 [
9898 Point::new(1, 3)..Point::new(1, 3),
9899 Point::new(2, 1)..Point::new(2, 1),
9900 ]
9901 );
9902 editor
9903 });
9904
9905 // Refreshing selections is a no-op when excerpts haven't changed.
9906 editor.update(cx, |editor, cx| {
9907 editor.refresh_selections(cx);
9908 assert_eq!(
9909 editor.selected_ranges(cx),
9910 [
9911 Point::new(1, 3)..Point::new(1, 3),
9912 Point::new(2, 1)..Point::new(2, 1),
9913 ]
9914 );
9915 });
9916
9917 multibuffer.update(cx, |multibuffer, cx| {
9918 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9919 });
9920 editor.update(cx, |editor, cx| {
9921 // Removing an excerpt causes the first selection to become degenerate.
9922 assert_eq!(
9923 editor.selected_ranges(cx),
9924 [
9925 Point::new(0, 0)..Point::new(0, 0),
9926 Point::new(0, 1)..Point::new(0, 1)
9927 ]
9928 );
9929
9930 // Refreshing selections will relocate the first selection to the original buffer
9931 // location.
9932 editor.refresh_selections(cx);
9933 assert_eq!(
9934 editor.selected_ranges(cx),
9935 [
9936 Point::new(0, 1)..Point::new(0, 1),
9937 Point::new(0, 3)..Point::new(0, 3)
9938 ]
9939 );
9940 assert!(editor.pending_selection.is_some());
9941 });
9942 }
9943
9944 #[gpui::test]
9945 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9946 cx.set_global(Settings::test(cx));
9947 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9948 let mut excerpt1_id = None;
9949 let multibuffer = cx.add_model(|cx| {
9950 let mut multibuffer = MultiBuffer::new(0);
9951 excerpt1_id = multibuffer
9952 .push_excerpts(
9953 buffer.clone(),
9954 [
9955 Point::new(0, 0)..Point::new(1, 4),
9956 Point::new(1, 0)..Point::new(2, 4),
9957 ],
9958 cx,
9959 )
9960 .into_iter()
9961 .next();
9962 multibuffer
9963 });
9964 assert_eq!(
9965 multibuffer.read(cx).read(cx).text(),
9966 "aaaa\nbbbb\nbbbb\ncccc"
9967 );
9968 let (_, editor) = cx.add_window(Default::default(), |cx| {
9969 let mut editor = build_editor(multibuffer.clone(), cx);
9970 let snapshot = editor.snapshot(cx);
9971 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9972 assert_eq!(
9973 editor.selected_ranges(cx),
9974 [Point::new(1, 3)..Point::new(1, 3)]
9975 );
9976 editor
9977 });
9978
9979 multibuffer.update(cx, |multibuffer, cx| {
9980 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9981 });
9982 editor.update(cx, |editor, cx| {
9983 assert_eq!(
9984 editor.selected_ranges(cx),
9985 [Point::new(0, 0)..Point::new(0, 0)]
9986 );
9987
9988 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9989 editor.refresh_selections(cx);
9990 assert_eq!(
9991 editor.selected_ranges(cx),
9992 [Point::new(0, 3)..Point::new(0, 3)]
9993 );
9994 assert!(editor.pending_selection.is_some());
9995 });
9996 }
9997
9998 #[gpui::test]
9999 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10000 cx.update(|cx| cx.set_global(Settings::test(cx)));
10001 let language = Arc::new(Language::new(
10002 LanguageConfig {
10003 brackets: vec![
10004 BracketPair {
10005 start: "{".to_string(),
10006 end: "}".to_string(),
10007 close: true,
10008 newline: true,
10009 },
10010 BracketPair {
10011 start: "/* ".to_string(),
10012 end: " */".to_string(),
10013 close: true,
10014 newline: true,
10015 },
10016 ],
10017 ..Default::default()
10018 },
10019 Some(tree_sitter_rust::language()),
10020 ));
10021
10022 let text = concat!(
10023 "{ }\n", // Suppress rustfmt
10024 " x\n", //
10025 " /* */\n", //
10026 "x\n", //
10027 "{{} }\n", //
10028 );
10029
10030 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10031 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10032 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10033 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10034 .await;
10035
10036 view.update(cx, |view, cx| {
10037 view.select_display_ranges(
10038 &[
10039 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10040 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10041 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10042 ],
10043 cx,
10044 );
10045 view.newline(&Newline, cx);
10046
10047 assert_eq!(
10048 view.buffer().read(cx).read(cx).text(),
10049 concat!(
10050 "{ \n", // Suppress rustfmt
10051 "\n", //
10052 "}\n", //
10053 " x\n", //
10054 " /* \n", //
10055 " \n", //
10056 " */\n", //
10057 "x\n", //
10058 "{{} \n", //
10059 "}\n", //
10060 )
10061 );
10062 });
10063 }
10064
10065 #[gpui::test]
10066 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10067 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10068
10069 cx.set_global(Settings::test(cx));
10070 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10071
10072 editor.update(cx, |editor, cx| {
10073 struct Type1;
10074 struct Type2;
10075
10076 let buffer = buffer.read(cx).snapshot(cx);
10077
10078 let anchor_range = |range: Range<Point>| {
10079 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10080 };
10081
10082 editor.highlight_background::<Type1>(
10083 vec![
10084 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10085 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10086 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10087 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10088 ],
10089 |_| Color::red(),
10090 cx,
10091 );
10092 editor.highlight_background::<Type2>(
10093 vec![
10094 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10095 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10096 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10097 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10098 ],
10099 |_| Color::green(),
10100 cx,
10101 );
10102
10103 let snapshot = editor.snapshot(cx);
10104 let mut highlighted_ranges = editor.background_highlights_in_range(
10105 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10106 &snapshot,
10107 cx.global::<Settings>().theme.as_ref(),
10108 );
10109 // Enforce a consistent ordering based on color without relying on the ordering of the
10110 // highlight's `TypeId` which is non-deterministic.
10111 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10112 assert_eq!(
10113 highlighted_ranges,
10114 &[
10115 (
10116 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10117 Color::green(),
10118 ),
10119 (
10120 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10121 Color::green(),
10122 ),
10123 (
10124 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10125 Color::red(),
10126 ),
10127 (
10128 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10129 Color::red(),
10130 ),
10131 ]
10132 );
10133 assert_eq!(
10134 editor.background_highlights_in_range(
10135 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10136 &snapshot,
10137 cx.global::<Settings>().theme.as_ref(),
10138 ),
10139 &[(
10140 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10141 Color::red(),
10142 )]
10143 );
10144 });
10145 }
10146
10147 #[gpui::test]
10148 fn test_following(cx: &mut gpui::MutableAppContext) {
10149 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10150
10151 cx.set_global(Settings::test(cx));
10152
10153 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10154 let (_, follower) = cx.add_window(
10155 WindowOptions {
10156 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10157 ..Default::default()
10158 },
10159 |cx| build_editor(buffer.clone(), cx),
10160 );
10161
10162 let pending_update = Rc::new(RefCell::new(None));
10163 follower.update(cx, {
10164 let update = pending_update.clone();
10165 |_, cx| {
10166 cx.subscribe(&leader, move |_, leader, event, cx| {
10167 leader
10168 .read(cx)
10169 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10170 })
10171 .detach();
10172 }
10173 });
10174
10175 // Update the selections only
10176 leader.update(cx, |leader, cx| {
10177 leader.select_ranges([1..1], None, cx);
10178 });
10179 follower.update(cx, |follower, cx| {
10180 follower
10181 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10182 .unwrap();
10183 });
10184 assert_eq!(follower.read(cx).selected_ranges(cx), vec![1..1]);
10185
10186 // Update the scroll position only
10187 leader.update(cx, |leader, cx| {
10188 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10189 });
10190 follower.update(cx, |follower, cx| {
10191 follower
10192 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10193 .unwrap();
10194 });
10195 assert_eq!(
10196 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10197 vec2f(1.5, 3.5)
10198 );
10199
10200 // Update the selections and scroll position
10201 leader.update(cx, |leader, cx| {
10202 leader.select_ranges([0..0], None, cx);
10203 leader.request_autoscroll(Autoscroll::Newest, cx);
10204 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10205 });
10206 follower.update(cx, |follower, cx| {
10207 let initial_scroll_position = follower.scroll_position(cx);
10208 follower
10209 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10210 .unwrap();
10211 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10212 assert!(follower.autoscroll_request.is_some());
10213 });
10214 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0]);
10215
10216 // Creating a pending selection that precedes another selection
10217 leader.update(cx, |leader, cx| {
10218 leader.select_ranges([1..1], None, cx);
10219 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10220 });
10221 follower.update(cx, |follower, cx| {
10222 follower
10223 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10224 .unwrap();
10225 });
10226 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0, 1..1]);
10227
10228 // Extend the pending selection so that it surrounds another selection
10229 leader.update(cx, |leader, cx| {
10230 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10231 });
10232 follower.update(cx, |follower, cx| {
10233 follower
10234 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10235 .unwrap();
10236 });
10237 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..2]);
10238 }
10239
10240 #[test]
10241 fn test_combine_syntax_and_fuzzy_match_highlights() {
10242 let string = "abcdefghijklmnop";
10243 let syntax_ranges = [
10244 (
10245 0..3,
10246 HighlightStyle {
10247 color: Some(Color::red()),
10248 ..Default::default()
10249 },
10250 ),
10251 (
10252 4..8,
10253 HighlightStyle {
10254 color: Some(Color::green()),
10255 ..Default::default()
10256 },
10257 ),
10258 ];
10259 let match_indices = [4, 6, 7, 8];
10260 assert_eq!(
10261 combine_syntax_and_fuzzy_match_highlights(
10262 &string,
10263 Default::default(),
10264 syntax_ranges.into_iter(),
10265 &match_indices,
10266 ),
10267 &[
10268 (
10269 0..3,
10270 HighlightStyle {
10271 color: Some(Color::red()),
10272 ..Default::default()
10273 },
10274 ),
10275 (
10276 4..5,
10277 HighlightStyle {
10278 color: Some(Color::green()),
10279 weight: Some(fonts::Weight::BOLD),
10280 ..Default::default()
10281 },
10282 ),
10283 (
10284 5..6,
10285 HighlightStyle {
10286 color: Some(Color::green()),
10287 ..Default::default()
10288 },
10289 ),
10290 (
10291 6..8,
10292 HighlightStyle {
10293 color: Some(Color::green()),
10294 weight: Some(fonts::Weight::BOLD),
10295 ..Default::default()
10296 },
10297 ),
10298 (
10299 8..9,
10300 HighlightStyle {
10301 weight: Some(fonts::Weight::BOLD),
10302 ..Default::default()
10303 },
10304 ),
10305 ]
10306 );
10307 }
10308
10309 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10310 let point = DisplayPoint::new(row as u32, column as u32);
10311 point..point
10312 }
10313
10314 fn build_editor(buffer: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
10315 Editor::new(EditorMode::Full, buffer, None, None, cx)
10316 }
10317
10318 fn assert_selection_ranges(
10319 marked_text: &str,
10320 selection_marker_pairs: Vec<(char, char)>,
10321 view: &mut Editor,
10322 cx: &mut ViewContext<Editor>,
10323 ) {
10324 let snapshot = view.snapshot(cx).display_snapshot;
10325 let mut marker_chars = Vec::new();
10326 for (start, end) in selection_marker_pairs.iter() {
10327 marker_chars.push(*start);
10328 marker_chars.push(*end);
10329 }
10330 let (_, markers) = marked_text_by(marked_text, marker_chars);
10331 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10332 .iter()
10333 .map(|(start, end)| {
10334 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10335 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10336 start..end
10337 })
10338 .collect();
10339 assert_eq!(
10340 view.selected_display_ranges(cx),
10341 &asserted_ranges[..],
10342 "Assert selections are {}",
10343 marked_text
10344 );
10345 }
10346}
10347
10348trait RangeExt<T> {
10349 fn sorted(&self) -> Range<T>;
10350 fn to_inclusive(&self) -> RangeInclusive<T>;
10351}
10352
10353impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10354 fn sorted(&self) -> Self {
10355 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10356 }
10357
10358 fn to_inclusive(&self) -> RangeInclusive<T> {
10359 self.start.clone()..=self.end.clone()
10360 }
10361}