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