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::Fit), 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
1972 .iter()
1973 .map(|s| (s.start.clone()..s.start.clone(), pair_start.clone()))
1974 .chain(
1975 selections
1976 .iter()
1977 .map(|s| (s.end.clone()..s.end.clone(), pair_end.clone())),
1978 ),
1979 cx,
1980 );
1981 });
1982
1983 let snapshot = self.buffer.read(cx).read(cx);
1984 for selection in &mut selections {
1985 selection.end = selection.end.bias_right(&snapshot);
1986 }
1987 drop(snapshot);
1988
1989 self.change_selections(None, cx, |s| s.select_anchors(selections));
1990 true
1991 } else {
1992 false
1993 }
1994 }
1995
1996 fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext<Self>) {
1997 let selections = self.selections.all::<usize>(cx);
1998 let mut bracket_pair_state = None;
1999 let mut new_selections = None;
2000 self.buffer.update(cx, |buffer, cx| {
2001 let mut snapshot = buffer.snapshot(cx);
2002 let left_biased_selections = selections
2003 .iter()
2004 .map(|selection| selection.map(|p| snapshot.anchor_before(p)))
2005 .collect::<Vec<_>>();
2006
2007 let autoclose_pair = snapshot.language().and_then(|language| {
2008 let first_selection_start = selections.first().unwrap().start;
2009 let pair = language.brackets().iter().find(|pair| {
2010 snapshot.contains_str_at(
2011 first_selection_start.saturating_sub(pair.start.len()),
2012 &pair.start,
2013 )
2014 });
2015 pair.and_then(|pair| {
2016 let should_autoclose = selections.iter().all(|selection| {
2017 // Ensure all selections are parked at the end of a pair start.
2018 if snapshot.contains_str_at(
2019 selection.start.saturating_sub(pair.start.len()),
2020 &pair.start,
2021 ) {
2022 snapshot
2023 .chars_at(selection.start)
2024 .next()
2025 .map_or(true, |c| language.should_autoclose_before(c))
2026 } else {
2027 false
2028 }
2029 });
2030
2031 if should_autoclose {
2032 Some(pair.clone())
2033 } else {
2034 None
2035 }
2036 })
2037 });
2038
2039 if let Some(pair) = autoclose_pair {
2040 let selection_ranges = selections
2041 .iter()
2042 .map(|selection| {
2043 let start = selection.start.to_offset(&snapshot);
2044 start..start
2045 })
2046 .collect::<SmallVec<[_; 32]>>();
2047
2048 let pair_end: Arc<str> = pair.end.clone().into();
2049 buffer.edit(
2050 selection_ranges
2051 .iter()
2052 .map(|range| (range.clone(), pair_end.clone())),
2053 cx,
2054 );
2055 snapshot = buffer.snapshot(cx);
2056
2057 new_selections = Some(
2058 resolve_multiple::<usize, _>(left_biased_selections.iter(), &snapshot)
2059 .collect::<Vec<_>>(),
2060 );
2061
2062 if pair.end.len() == 1 {
2063 let mut delta = 0;
2064 bracket_pair_state = Some(BracketPairState {
2065 ranges: selections
2066 .iter()
2067 .map(move |selection| {
2068 let offset = selection.start + delta;
2069 delta += 1;
2070 snapshot.anchor_before(offset)..snapshot.anchor_after(offset)
2071 })
2072 .collect(),
2073 pair,
2074 });
2075 }
2076 }
2077 });
2078
2079 if let Some(new_selections) = new_selections {
2080 self.change_selections(None, cx, |s| {
2081 s.select(new_selections);
2082 });
2083 }
2084 if let Some(bracket_pair_state) = bracket_pair_state {
2085 self.autoclose_stack.push(bracket_pair_state);
2086 }
2087 }
2088
2089 fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
2090 let buffer = self.buffer.read(cx).snapshot(cx);
2091 let old_selections = self.selections.all::<usize>(cx);
2092 let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
2093 autoclose_pair
2094 } else {
2095 return false;
2096 };
2097 if text != autoclose_pair.pair.end {
2098 return false;
2099 }
2100
2101 debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
2102
2103 if old_selections
2104 .iter()
2105 .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
2106 .all(|(selection, autoclose_range)| {
2107 let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
2108 selection.is_empty() && selection.start == autoclose_range_end
2109 })
2110 {
2111 let new_selections = old_selections
2112 .into_iter()
2113 .map(|selection| {
2114 let cursor = selection.start + 1;
2115 Selection {
2116 id: selection.id,
2117 start: cursor,
2118 end: cursor,
2119 reversed: false,
2120 goal: SelectionGoal::None,
2121 }
2122 })
2123 .collect();
2124 self.autoclose_stack.pop();
2125 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2126 s.select(new_selections);
2127 });
2128 true
2129 } else {
2130 false
2131 }
2132 }
2133
2134 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2135 let offset = position.to_offset(buffer);
2136 let (word_range, kind) = buffer.surrounding_word(offset);
2137 if offset > word_range.start && kind == Some(CharKind::Word) {
2138 Some(
2139 buffer
2140 .text_for_range(word_range.start..offset)
2141 .collect::<String>(),
2142 )
2143 } else {
2144 None
2145 }
2146 }
2147
2148 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2149 if self.pending_rename.is_some() {
2150 return;
2151 }
2152
2153 let project = if let Some(project) = self.project.clone() {
2154 project
2155 } else {
2156 return;
2157 };
2158
2159 let position = self.selections.newest_anchor().head();
2160 let (buffer, buffer_position) = if let Some(output) = self
2161 .buffer
2162 .read(cx)
2163 .text_anchor_for_position(position.clone(), cx)
2164 {
2165 output
2166 } else {
2167 return;
2168 };
2169
2170 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2171 let completions = project.update(cx, |project, cx| {
2172 project.completions(&buffer, buffer_position.clone(), cx)
2173 });
2174
2175 let id = post_inc(&mut self.next_completion_id);
2176 let task = cx.spawn_weak(|this, mut cx| {
2177 async move {
2178 let completions = completions.await?;
2179 if completions.is_empty() {
2180 return Ok(());
2181 }
2182
2183 let mut menu = CompletionsMenu {
2184 id,
2185 initial_position: position,
2186 match_candidates: completions
2187 .iter()
2188 .enumerate()
2189 .map(|(id, completion)| {
2190 StringMatchCandidate::new(
2191 id,
2192 completion.label.text[completion.label.filter_range.clone()].into(),
2193 )
2194 })
2195 .collect(),
2196 buffer,
2197 completions: completions.into(),
2198 matches: Vec::new().into(),
2199 selected_item: 0,
2200 list: Default::default(),
2201 };
2202
2203 menu.filter(query.as_deref(), cx.background()).await;
2204
2205 if let Some(this) = this.upgrade(&cx) {
2206 this.update(&mut cx, |this, cx| {
2207 match this.context_menu.as_ref() {
2208 None => {}
2209 Some(ContextMenu::Completions(prev_menu)) => {
2210 if prev_menu.id > menu.id {
2211 return;
2212 }
2213 }
2214 _ => return,
2215 }
2216
2217 this.completion_tasks.retain(|(id, _)| *id > menu.id);
2218 if this.focused {
2219 this.show_context_menu(ContextMenu::Completions(menu), cx);
2220 }
2221
2222 cx.notify();
2223 });
2224 }
2225 Ok::<_, anyhow::Error>(())
2226 }
2227 .log_err()
2228 });
2229 self.completion_tasks.push((id, task));
2230 }
2231
2232 pub fn confirm_completion(
2233 &mut self,
2234 action: &ConfirmCompletion,
2235 cx: &mut ViewContext<Self>,
2236 ) -> Option<Task<Result<()>>> {
2237 use language::ToOffset as _;
2238
2239 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2240 menu
2241 } else {
2242 return None;
2243 };
2244
2245 let mat = completions_menu
2246 .matches
2247 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2248 let buffer_handle = completions_menu.buffer;
2249 let completion = completions_menu.completions.get(mat.candidate_id)?;
2250
2251 let snippet;
2252 let text;
2253 if completion.is_snippet() {
2254 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2255 text = snippet.as_ref().unwrap().text.clone();
2256 } else {
2257 snippet = None;
2258 text = completion.new_text.clone();
2259 };
2260 let selections = self.selections.all::<usize>(cx);
2261 let buffer = buffer_handle.read(cx);
2262 let old_range = completion.old_range.to_offset(&buffer);
2263 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2264
2265 let newest_selection = self.selections.newest_anchor();
2266 if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
2267 return None;
2268 }
2269
2270 let lookbehind = newest_selection
2271 .start
2272 .text_anchor
2273 .to_offset(buffer)
2274 .saturating_sub(old_range.start);
2275 let lookahead = old_range
2276 .end
2277 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2278 let mut common_prefix_len = old_text
2279 .bytes()
2280 .zip(text.bytes())
2281 .take_while(|(a, b)| a == b)
2282 .count();
2283
2284 let snapshot = self.buffer.read(cx).snapshot(cx);
2285 let mut ranges = Vec::new();
2286 for selection in &selections {
2287 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2288 let start = selection.start.saturating_sub(lookbehind);
2289 let end = selection.end + lookahead;
2290 ranges.push(start + common_prefix_len..end);
2291 } else {
2292 common_prefix_len = 0;
2293 ranges.clear();
2294 ranges.extend(selections.iter().map(|s| {
2295 if s.id == newest_selection.id {
2296 old_range.clone()
2297 } else {
2298 s.start..s.end
2299 }
2300 }));
2301 break;
2302 }
2303 }
2304 let text = &text[common_prefix_len..];
2305
2306 self.transact(cx, |this, cx| {
2307 if let Some(mut snippet) = snippet {
2308 snippet.text = text.to_string();
2309 for tabstop in snippet.tabstops.iter_mut().flatten() {
2310 tabstop.start -= common_prefix_len as isize;
2311 tabstop.end -= common_prefix_len as isize;
2312 }
2313
2314 this.insert_snippet(&ranges, snippet, cx).log_err();
2315 } else {
2316 this.buffer.update(cx, |buffer, cx| {
2317 buffer
2318 .edit_with_autoindent(ranges.iter().map(|range| (range.clone(), text)), cx);
2319 });
2320 }
2321 });
2322
2323 let project = self.project.clone()?;
2324 let apply_edits = project.update(cx, |project, cx| {
2325 project.apply_additional_edits_for_completion(
2326 buffer_handle,
2327 completion.clone(),
2328 true,
2329 cx,
2330 )
2331 });
2332 Some(cx.foreground().spawn(async move {
2333 apply_edits.await?;
2334 Ok(())
2335 }))
2336 }
2337
2338 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2339 if matches!(
2340 self.context_menu.as_ref(),
2341 Some(ContextMenu::CodeActions(_))
2342 ) {
2343 self.context_menu.take();
2344 cx.notify();
2345 return;
2346 }
2347
2348 let deployed_from_indicator = action.deployed_from_indicator;
2349 let mut task = self.code_actions_task.take();
2350 cx.spawn_weak(|this, mut cx| async move {
2351 while let Some(prev_task) = task {
2352 prev_task.await;
2353 task = this
2354 .upgrade(&cx)
2355 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2356 }
2357
2358 if let Some(this) = this.upgrade(&cx) {
2359 this.update(&mut cx, |this, cx| {
2360 if this.focused {
2361 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2362 this.show_context_menu(
2363 ContextMenu::CodeActions(CodeActionsMenu {
2364 buffer,
2365 actions,
2366 selected_item: Default::default(),
2367 list: Default::default(),
2368 deployed_from_indicator,
2369 }),
2370 cx,
2371 );
2372 }
2373 }
2374 })
2375 }
2376 Ok::<_, anyhow::Error>(())
2377 })
2378 .detach_and_log_err(cx);
2379 }
2380
2381 pub fn confirm_code_action(
2382 workspace: &mut Workspace,
2383 action: &ConfirmCodeAction,
2384 cx: &mut ViewContext<Workspace>,
2385 ) -> Option<Task<Result<()>>> {
2386 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2387 let actions_menu = if let ContextMenu::CodeActions(menu) =
2388 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2389 {
2390 menu
2391 } else {
2392 return None;
2393 };
2394 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2395 let action = actions_menu.actions.get(action_ix)?.clone();
2396 let title = action.lsp_action.title.clone();
2397 let buffer = actions_menu.buffer;
2398
2399 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2400 project.apply_code_action(buffer, action, true, cx)
2401 });
2402 Some(cx.spawn(|workspace, cx| async move {
2403 let project_transaction = apply_code_actions.await?;
2404 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2405 }))
2406 }
2407
2408 async fn open_project_transaction(
2409 this: ViewHandle<Editor>,
2410 workspace: ViewHandle<Workspace>,
2411 transaction: ProjectTransaction,
2412 title: String,
2413 mut cx: AsyncAppContext,
2414 ) -> Result<()> {
2415 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2416
2417 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2418 entries.sort_unstable_by_key(|(buffer, _)| {
2419 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2420 });
2421
2422 // If the project transaction's edits are all contained within this editor, then
2423 // avoid opening a new editor to display them.
2424
2425 if let Some((buffer, transaction)) = entries.first() {
2426 if entries.len() == 1 {
2427 let excerpt = this.read_with(&cx, |editor, cx| {
2428 editor
2429 .buffer()
2430 .read(cx)
2431 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2432 });
2433 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2434 if excerpted_buffer == *buffer {
2435 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
2436 let excerpt_range = excerpt_range.to_offset(&snapshot);
2437 if snapshot
2438 .edited_ranges_for_transaction(transaction)
2439 .all(|range| {
2440 excerpt_range.start <= range.start && excerpt_range.end >= range.end
2441 })
2442 {
2443 return Ok(());
2444 }
2445 }
2446 }
2447 }
2448 } else {
2449 return Ok(());
2450 }
2451
2452 let mut ranges_to_highlight = Vec::new();
2453 let excerpt_buffer = cx.add_model(|cx| {
2454 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2455 for (buffer, transaction) in &entries {
2456 let snapshot = buffer.read(cx).snapshot();
2457 ranges_to_highlight.extend(
2458 multibuffer.push_excerpts_with_context_lines(
2459 buffer.clone(),
2460 snapshot
2461 .edited_ranges_for_transaction::<usize>(transaction)
2462 .collect(),
2463 1,
2464 cx,
2465 ),
2466 );
2467 }
2468 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2469 multibuffer
2470 });
2471
2472 workspace.update(&mut cx, |workspace, cx| {
2473 let project = workspace.project().clone();
2474 let editor =
2475 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2476 workspace.add_item(Box::new(editor.clone()), cx);
2477 editor.update(cx, |editor, cx| {
2478 editor.highlight_background::<Self>(
2479 ranges_to_highlight,
2480 |theme| theme.editor.highlighted_line_background,
2481 cx,
2482 );
2483 });
2484 });
2485
2486 Ok(())
2487 }
2488
2489 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2490 let project = self.project.as_ref()?;
2491 let buffer = self.buffer.read(cx);
2492 let newest_selection = self.selections.newest_anchor().clone();
2493 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2494 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2495 if start_buffer != end_buffer {
2496 return None;
2497 }
2498
2499 let actions = project.update(cx, |project, cx| {
2500 project.code_actions(&start_buffer, start..end, cx)
2501 });
2502 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2503 let actions = actions.await;
2504 if let Some(this) = this.upgrade(&cx) {
2505 this.update(&mut cx, |this, cx| {
2506 this.available_code_actions = actions.log_err().and_then(|actions| {
2507 if actions.is_empty() {
2508 None
2509 } else {
2510 Some((start_buffer, actions.into()))
2511 }
2512 });
2513 cx.notify();
2514 })
2515 }
2516 }));
2517 None
2518 }
2519
2520 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2521 if self.pending_rename.is_some() {
2522 return None;
2523 }
2524
2525 let project = self.project.as_ref()?;
2526 let buffer = self.buffer.read(cx);
2527 let newest_selection = self.selections.newest_anchor().clone();
2528 let cursor_position = newest_selection.head();
2529 let (cursor_buffer, cursor_buffer_position) =
2530 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2531 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2532 if cursor_buffer != tail_buffer {
2533 return None;
2534 }
2535
2536 let highlights = project.update(cx, |project, cx| {
2537 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2538 });
2539
2540 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2541 let highlights = highlights.log_err().await;
2542 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2543 this.update(&mut cx, |this, cx| {
2544 if this.pending_rename.is_some() {
2545 return;
2546 }
2547
2548 let buffer_id = cursor_position.buffer_id;
2549 let buffer = this.buffer.read(cx);
2550 if !buffer
2551 .text_anchor_for_position(cursor_position, cx)
2552 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2553 {
2554 return;
2555 }
2556
2557 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2558 let mut write_ranges = Vec::new();
2559 let mut read_ranges = Vec::new();
2560 for highlight in highlights {
2561 for (excerpt_id, excerpt_range) in
2562 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2563 {
2564 let start = highlight
2565 .range
2566 .start
2567 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
2568 let end = highlight
2569 .range
2570 .end
2571 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
2572 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2573 continue;
2574 }
2575
2576 let range = Anchor {
2577 buffer_id,
2578 excerpt_id: excerpt_id.clone(),
2579 text_anchor: start,
2580 }..Anchor {
2581 buffer_id,
2582 excerpt_id,
2583 text_anchor: end,
2584 };
2585 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2586 write_ranges.push(range);
2587 } else {
2588 read_ranges.push(range);
2589 }
2590 }
2591 }
2592
2593 this.highlight_background::<DocumentHighlightRead>(
2594 read_ranges,
2595 |theme| theme.editor.document_highlight_read_background,
2596 cx,
2597 );
2598 this.highlight_background::<DocumentHighlightWrite>(
2599 write_ranges,
2600 |theme| theme.editor.document_highlight_write_background,
2601 cx,
2602 );
2603 cx.notify();
2604 });
2605 }
2606 }));
2607 None
2608 }
2609
2610 pub fn render_code_actions_indicator(
2611 &self,
2612 style: &EditorStyle,
2613 cx: &mut RenderContext<Self>,
2614 ) -> Option<ElementBox> {
2615 if self.available_code_actions.is_some() {
2616 enum Tag {}
2617 Some(
2618 MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
2619 Svg::new("icons/zap.svg")
2620 .with_color(style.code_actions_indicator)
2621 .boxed()
2622 })
2623 .with_cursor_style(CursorStyle::PointingHand)
2624 .with_padding(Padding::uniform(3.))
2625 .on_mouse_down(|_, cx| {
2626 cx.dispatch_action(ToggleCodeActions {
2627 deployed_from_indicator: true,
2628 });
2629 })
2630 .boxed(),
2631 )
2632 } else {
2633 None
2634 }
2635 }
2636
2637 pub fn context_menu_visible(&self) -> bool {
2638 self.context_menu
2639 .as_ref()
2640 .map_or(false, |menu| menu.visible())
2641 }
2642
2643 pub fn render_context_menu(
2644 &self,
2645 cursor_position: DisplayPoint,
2646 style: EditorStyle,
2647 cx: &mut RenderContext<Editor>,
2648 ) -> Option<(DisplayPoint, ElementBox)> {
2649 self.context_menu
2650 .as_ref()
2651 .map(|menu| menu.render(cursor_position, style, cx))
2652 }
2653
2654 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
2655 if !matches!(menu, ContextMenu::Completions(_)) {
2656 self.completion_tasks.clear();
2657 }
2658 self.context_menu = Some(menu);
2659 cx.notify();
2660 }
2661
2662 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
2663 cx.notify();
2664 self.completion_tasks.clear();
2665 self.context_menu.take()
2666 }
2667
2668 pub fn insert_snippet(
2669 &mut self,
2670 insertion_ranges: &[Range<usize>],
2671 snippet: Snippet,
2672 cx: &mut ViewContext<Self>,
2673 ) -> Result<()> {
2674 let tabstops = self.buffer.update(cx, |buffer, cx| {
2675 let snippet_text: Arc<str> = snippet.text.clone().into();
2676 buffer.edit_with_autoindent(
2677 insertion_ranges
2678 .iter()
2679 .cloned()
2680 .map(|range| (range, snippet_text.clone())),
2681 cx,
2682 );
2683
2684 let snapshot = &*buffer.read(cx);
2685 let snippet = &snippet;
2686 snippet
2687 .tabstops
2688 .iter()
2689 .map(|tabstop| {
2690 let mut tabstop_ranges = tabstop
2691 .iter()
2692 .flat_map(|tabstop_range| {
2693 let mut delta = 0 as isize;
2694 insertion_ranges.iter().map(move |insertion_range| {
2695 let insertion_start = insertion_range.start as isize + delta;
2696 delta +=
2697 snippet.text.len() as isize - insertion_range.len() as isize;
2698
2699 let start = snapshot.anchor_before(
2700 (insertion_start + tabstop_range.start) as usize,
2701 );
2702 let end = snapshot
2703 .anchor_after((insertion_start + tabstop_range.end) as usize);
2704 start..end
2705 })
2706 })
2707 .collect::<Vec<_>>();
2708 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
2709 tabstop_ranges
2710 })
2711 .collect::<Vec<_>>()
2712 });
2713
2714 if let Some(tabstop) = tabstops.first() {
2715 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2716 s.select_ranges(tabstop.iter().cloned());
2717 });
2718 self.snippet_stack.push(SnippetState {
2719 active_index: 0,
2720 ranges: tabstops,
2721 });
2722 }
2723
2724 Ok(())
2725 }
2726
2727 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2728 self.move_to_snippet_tabstop(Bias::Right, cx)
2729 }
2730
2731 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2732 self.move_to_snippet_tabstop(Bias::Left, cx)
2733 }
2734
2735 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
2736 if let Some(mut snippet) = self.snippet_stack.pop() {
2737 match bias {
2738 Bias::Left => {
2739 if snippet.active_index > 0 {
2740 snippet.active_index -= 1;
2741 } else {
2742 self.snippet_stack.push(snippet);
2743 return false;
2744 }
2745 }
2746 Bias::Right => {
2747 if snippet.active_index + 1 < snippet.ranges.len() {
2748 snippet.active_index += 1;
2749 } else {
2750 self.snippet_stack.push(snippet);
2751 return false;
2752 }
2753 }
2754 }
2755 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
2756 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2757 s.select_anchor_ranges(current_ranges.into_iter().cloned())
2758 });
2759 // If snippet state is not at the last tabstop, push it back on the stack
2760 if snippet.active_index + 1 < snippet.ranges.len() {
2761 self.snippet_stack.push(snippet);
2762 }
2763 return true;
2764 }
2765 }
2766
2767 false
2768 }
2769
2770 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
2771 self.transact(cx, |this, cx| {
2772 this.select_all(&SelectAll, cx);
2773 this.insert("", cx);
2774 });
2775 }
2776
2777 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
2778 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2779 let mut selections = self.selections.all::<Point>(cx);
2780 if !self.selections.line_mode {
2781 for selection in &mut selections {
2782 if selection.is_empty() {
2783 let old_head = selection.head();
2784 let mut new_head =
2785 movement::left(&display_map, old_head.to_display_point(&display_map))
2786 .to_point(&display_map);
2787 if let Some((buffer, line_buffer_range)) = display_map
2788 .buffer_snapshot
2789 .buffer_line_for_row(old_head.row)
2790 {
2791 let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
2792 let language_name = buffer.language().map(|language| language.name());
2793 let indent_len = match indent_size.kind {
2794 IndentKind::Space => {
2795 cx.global::<Settings>().tab_size(language_name.as_deref())
2796 }
2797 IndentKind::Tab => 1,
2798 };
2799 if old_head.column <= indent_size.len && old_head.column > 0 {
2800 new_head = cmp::min(
2801 new_head,
2802 Point::new(
2803 old_head.row,
2804 ((old_head.column - 1) / indent_len) * indent_len,
2805 ),
2806 );
2807 }
2808 }
2809
2810 selection.set_head(new_head, SelectionGoal::None);
2811 }
2812 }
2813 }
2814
2815 self.transact(cx, |this, cx| {
2816 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
2817 this.insert("", cx);
2818 });
2819 }
2820
2821 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2822 self.transact(cx, |this, cx| {
2823 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2824 let line_mode = s.line_mode;
2825 s.move_with(|map, selection| {
2826 if selection.is_empty() && !line_mode {
2827 let cursor = movement::right(map, selection.head());
2828 selection.set_head(cursor, SelectionGoal::None);
2829 }
2830 })
2831 });
2832 this.insert(&"", cx);
2833 });
2834 }
2835
2836 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2837 if self.move_to_prev_snippet_tabstop(cx) {
2838 return;
2839 }
2840
2841 self.outdent(&Outdent, cx);
2842 }
2843
2844 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2845 if self.move_to_next_snippet_tabstop(cx) {
2846 return;
2847 }
2848
2849 let mut selections = self.selections.all_adjusted(cx);
2850 if selections.iter().all(|s| s.is_empty()) {
2851 self.transact(cx, |this, cx| {
2852 this.buffer.update(cx, |buffer, cx| {
2853 for selection in &mut selections {
2854 let language_name =
2855 buffer.language_at(selection.start, cx).map(|l| l.name());
2856 let settings = cx.global::<Settings>();
2857 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
2858 IndentSize::tab()
2859 } else {
2860 let tab_size = settings.tab_size(language_name.as_deref());
2861 let char_column = buffer
2862 .read(cx)
2863 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
2864 .flat_map(str::chars)
2865 .count();
2866 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
2867 IndentSize::spaces(chars_to_next_tab_stop)
2868 };
2869 buffer.edit(
2870 [(
2871 selection.start..selection.start,
2872 tab_size.chars().collect::<String>(),
2873 )],
2874 cx,
2875 );
2876 selection.start.column += tab_size.len;
2877 selection.end = selection.start;
2878 }
2879 });
2880 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2881 s.select(selections);
2882 });
2883 });
2884 } else {
2885 self.indent(&Indent, cx);
2886 }
2887 }
2888
2889 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
2890 let mut selections = self.selections.all::<Point>(cx);
2891 self.transact(cx, |this, cx| {
2892 let mut last_indent = None;
2893 this.buffer.update(cx, |buffer, cx| {
2894 let snapshot = buffer.snapshot(cx);
2895 for selection in &mut selections {
2896 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2897 let settings = &cx.global::<Settings>();
2898 let tab_size = settings.tab_size(language_name.as_deref());
2899 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
2900 IndentKind::Tab
2901 } else {
2902 IndentKind::Space
2903 };
2904
2905 let mut start_row = selection.start.row;
2906 let mut end_row = selection.end.row + 1;
2907
2908 // If a selection ends at the beginning of a line, don't indent
2909 // that last line.
2910 if selection.end.column == 0 {
2911 end_row -= 1;
2912 }
2913
2914 // Avoid re-indenting a row that has already been indented by a
2915 // previous selection, but still update this selection's column
2916 // to reflect that indentation.
2917 if let Some((last_indent_row, last_indent_len)) = last_indent {
2918 if last_indent_row == selection.start.row {
2919 selection.start.column += last_indent_len;
2920 start_row += 1;
2921 }
2922 if last_indent_row == selection.end.row {
2923 selection.end.column += last_indent_len;
2924 }
2925 }
2926
2927 for row in start_row..end_row {
2928 let current_indent = snapshot.indent_size_for_line(row);
2929 let indent_delta = match (current_indent.kind, indent_kind) {
2930 (IndentKind::Space, IndentKind::Space) => {
2931 let columns_to_next_tab_stop =
2932 tab_size - (current_indent.len % tab_size);
2933 IndentSize::spaces(columns_to_next_tab_stop)
2934 }
2935 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
2936 (_, IndentKind::Tab) => IndentSize::tab(),
2937 };
2938
2939 let row_start = Point::new(row, 0);
2940 buffer.edit(
2941 [(
2942 row_start..row_start,
2943 indent_delta.chars().collect::<String>(),
2944 )],
2945 cx,
2946 );
2947
2948 // Update this selection's endpoints to reflect the indentation.
2949 if row == selection.start.row {
2950 selection.start.column += indent_delta.len;
2951 }
2952 if row == selection.end.row {
2953 selection.end.column += indent_delta.len as u32;
2954 }
2955
2956 last_indent = Some((row, indent_delta.len));
2957 }
2958 }
2959 });
2960
2961 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2962 s.select(selections);
2963 });
2964 });
2965 }
2966
2967 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
2968 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2969 let selections = self.selections.all::<Point>(cx);
2970 let mut deletion_ranges = Vec::new();
2971 let mut last_outdent = None;
2972 {
2973 let buffer = self.buffer.read(cx);
2974 let snapshot = buffer.snapshot(cx);
2975 for selection in &selections {
2976 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2977 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2978 let mut rows = selection.spanned_rows(false, &display_map);
2979
2980 // Avoid re-outdenting a row that has already been outdented by a
2981 // previous selection.
2982 if let Some(last_row) = last_outdent {
2983 if last_row == rows.start {
2984 rows.start += 1;
2985 }
2986 }
2987
2988 for row in rows {
2989 let indent_size = snapshot.indent_size_for_line(row);
2990 if indent_size.len > 0 {
2991 let deletion_len = match indent_size.kind {
2992 IndentKind::Space => {
2993 let columns_to_prev_tab_stop = indent_size.len % tab_size;
2994 if columns_to_prev_tab_stop == 0 {
2995 tab_size
2996 } else {
2997 columns_to_prev_tab_stop
2998 }
2999 }
3000 IndentKind::Tab => 1,
3001 };
3002 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3003 last_outdent = Some(row);
3004 }
3005 }
3006 }
3007 }
3008
3009 self.transact(cx, |this, cx| {
3010 this.buffer.update(cx, |buffer, cx| {
3011 let empty_str: Arc<str> = "".into();
3012 buffer.edit(
3013 deletion_ranges
3014 .into_iter()
3015 .map(|range| (range, empty_str.clone())),
3016 cx,
3017 );
3018 });
3019 let selections = this.selections.all::<usize>(cx);
3020 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3021 });
3022 }
3023
3024 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3025 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3026 let selections = self.selections.all::<Point>(cx);
3027
3028 let mut new_cursors = Vec::new();
3029 let mut edit_ranges = Vec::new();
3030 let mut selections = selections.iter().peekable();
3031 while let Some(selection) = selections.next() {
3032 let mut rows = selection.spanned_rows(false, &display_map);
3033 let goal_display_column = selection.head().to_display_point(&display_map).column();
3034
3035 // Accumulate contiguous regions of rows that we want to delete.
3036 while let Some(next_selection) = selections.peek() {
3037 let next_rows = next_selection.spanned_rows(false, &display_map);
3038 if next_rows.start <= rows.end {
3039 rows.end = next_rows.end;
3040 selections.next().unwrap();
3041 } else {
3042 break;
3043 }
3044 }
3045
3046 let buffer = &display_map.buffer_snapshot;
3047 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3048 let edit_end;
3049 let cursor_buffer_row;
3050 if buffer.max_point().row >= rows.end {
3051 // If there's a line after the range, delete the \n from the end of the row range
3052 // and position the cursor on the next line.
3053 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3054 cursor_buffer_row = rows.end;
3055 } else {
3056 // If there isn't a line after the range, delete the \n from the line before the
3057 // start of the row range and position the cursor there.
3058 edit_start = edit_start.saturating_sub(1);
3059 edit_end = buffer.len();
3060 cursor_buffer_row = rows.start.saturating_sub(1);
3061 }
3062
3063 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3064 *cursor.column_mut() =
3065 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3066
3067 new_cursors.push((
3068 selection.id,
3069 buffer.anchor_after(cursor.to_point(&display_map)),
3070 ));
3071 edit_ranges.push(edit_start..edit_end);
3072 }
3073
3074 self.transact(cx, |this, cx| {
3075 let buffer = this.buffer.update(cx, |buffer, cx| {
3076 let empty_str: Arc<str> = "".into();
3077 buffer.edit(
3078 edit_ranges
3079 .into_iter()
3080 .map(|range| (range, empty_str.clone())),
3081 cx,
3082 );
3083 buffer.snapshot(cx)
3084 });
3085 let new_selections = new_cursors
3086 .into_iter()
3087 .map(|(id, cursor)| {
3088 let cursor = cursor.to_point(&buffer);
3089 Selection {
3090 id,
3091 start: cursor,
3092 end: cursor,
3093 reversed: false,
3094 goal: SelectionGoal::None,
3095 }
3096 })
3097 .collect();
3098
3099 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3100 s.select(new_selections);
3101 });
3102 });
3103 }
3104
3105 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3106 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3107 let buffer = &display_map.buffer_snapshot;
3108 let selections = self.selections.all::<Point>(cx);
3109
3110 let mut edits = Vec::new();
3111 let mut selections_iter = selections.iter().peekable();
3112 while let Some(selection) = selections_iter.next() {
3113 // Avoid duplicating the same lines twice.
3114 let mut rows = selection.spanned_rows(false, &display_map);
3115
3116 while let Some(next_selection) = selections_iter.peek() {
3117 let next_rows = next_selection.spanned_rows(false, &display_map);
3118 if next_rows.start <= rows.end - 1 {
3119 rows.end = next_rows.end;
3120 selections_iter.next().unwrap();
3121 } else {
3122 break;
3123 }
3124 }
3125
3126 // Copy the text from the selected row region and splice it at the start of the region.
3127 let start = Point::new(rows.start, 0);
3128 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3129 let text = buffer
3130 .text_for_range(start..end)
3131 .chain(Some("\n"))
3132 .collect::<String>();
3133 edits.push((start..start, text));
3134 }
3135
3136 self.transact(cx, |this, cx| {
3137 this.buffer.update(cx, |buffer, cx| {
3138 buffer.edit(edits, cx);
3139 });
3140
3141 this.request_autoscroll(Autoscroll::Fit, cx);
3142 });
3143 }
3144
3145 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3146 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3147 let buffer = self.buffer.read(cx).snapshot(cx);
3148
3149 let mut edits = Vec::new();
3150 let mut unfold_ranges = Vec::new();
3151 let mut refold_ranges = Vec::new();
3152
3153 let selections = self.selections.all::<Point>(cx);
3154 let mut selections = selections.iter().peekable();
3155 let mut contiguous_row_selections = Vec::new();
3156 let mut new_selections = Vec::new();
3157
3158 while let Some(selection) = selections.next() {
3159 // Find all the selections that span a contiguous row range
3160 contiguous_row_selections.push(selection.clone());
3161 let start_row = selection.start.row;
3162 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3163 display_map.next_line_boundary(selection.end).0.row + 1
3164 } else {
3165 selection.end.row
3166 };
3167
3168 while let Some(next_selection) = selections.peek() {
3169 if next_selection.start.row <= end_row {
3170 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3171 display_map.next_line_boundary(next_selection.end).0.row + 1
3172 } else {
3173 next_selection.end.row
3174 };
3175 contiguous_row_selections.push(selections.next().unwrap().clone());
3176 } else {
3177 break;
3178 }
3179 }
3180
3181 // Move the text spanned by the row range to be before the line preceding the row range
3182 if start_row > 0 {
3183 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3184 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3185 let insertion_point = display_map
3186 .prev_line_boundary(Point::new(start_row - 1, 0))
3187 .0;
3188
3189 // Don't move lines across excerpts
3190 if buffer
3191 .excerpt_boundaries_in_range((
3192 Bound::Excluded(insertion_point),
3193 Bound::Included(range_to_move.end),
3194 ))
3195 .next()
3196 .is_none()
3197 {
3198 let text = buffer
3199 .text_for_range(range_to_move.clone())
3200 .flat_map(|s| s.chars())
3201 .skip(1)
3202 .chain(['\n'])
3203 .collect::<String>();
3204
3205 edits.push((
3206 buffer.anchor_after(range_to_move.start)
3207 ..buffer.anchor_before(range_to_move.end),
3208 String::new(),
3209 ));
3210 let insertion_anchor = buffer.anchor_after(insertion_point);
3211 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3212
3213 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3214
3215 // Move selections up
3216 new_selections.extend(contiguous_row_selections.drain(..).map(
3217 |mut selection| {
3218 selection.start.row -= row_delta;
3219 selection.end.row -= row_delta;
3220 selection
3221 },
3222 ));
3223
3224 // Move folds up
3225 unfold_ranges.push(range_to_move.clone());
3226 for fold in display_map.folds_in_range(
3227 buffer.anchor_before(range_to_move.start)
3228 ..buffer.anchor_after(range_to_move.end),
3229 ) {
3230 let mut start = fold.start.to_point(&buffer);
3231 let mut end = fold.end.to_point(&buffer);
3232 start.row -= row_delta;
3233 end.row -= row_delta;
3234 refold_ranges.push(start..end);
3235 }
3236 }
3237 }
3238
3239 // If we didn't move line(s), preserve the existing selections
3240 new_selections.extend(contiguous_row_selections.drain(..));
3241 }
3242
3243 self.transact(cx, |this, cx| {
3244 this.unfold_ranges(unfold_ranges, true, cx);
3245 this.buffer.update(cx, |buffer, cx| {
3246 for (range, text) in edits {
3247 buffer.edit([(range, text)], cx);
3248 }
3249 });
3250 this.fold_ranges(refold_ranges, cx);
3251 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3252 s.select(new_selections);
3253 })
3254 });
3255 }
3256
3257 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3258 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3259 let buffer = self.buffer.read(cx).snapshot(cx);
3260
3261 let mut edits = Vec::new();
3262 let mut unfold_ranges = Vec::new();
3263 let mut refold_ranges = Vec::new();
3264
3265 let selections = self.selections.all::<Point>(cx);
3266 let mut selections = selections.iter().peekable();
3267 let mut contiguous_row_selections = Vec::new();
3268 let mut new_selections = Vec::new();
3269
3270 while let Some(selection) = selections.next() {
3271 // Find all the selections that span a contiguous row range
3272 contiguous_row_selections.push(selection.clone());
3273 let start_row = selection.start.row;
3274 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3275 display_map.next_line_boundary(selection.end).0.row + 1
3276 } else {
3277 selection.end.row
3278 };
3279
3280 while let Some(next_selection) = selections.peek() {
3281 if next_selection.start.row <= end_row {
3282 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3283 display_map.next_line_boundary(next_selection.end).0.row + 1
3284 } else {
3285 next_selection.end.row
3286 };
3287 contiguous_row_selections.push(selections.next().unwrap().clone());
3288 } else {
3289 break;
3290 }
3291 }
3292
3293 // Move the text spanned by the row range to be after the last line of the row range
3294 if end_row <= buffer.max_point().row {
3295 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3296 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3297
3298 // Don't move lines across excerpt boundaries
3299 if buffer
3300 .excerpt_boundaries_in_range((
3301 Bound::Excluded(range_to_move.start),
3302 Bound::Included(insertion_point),
3303 ))
3304 .next()
3305 .is_none()
3306 {
3307 let mut text = String::from("\n");
3308 text.extend(buffer.text_for_range(range_to_move.clone()));
3309 text.pop(); // Drop trailing newline
3310 edits.push((
3311 buffer.anchor_after(range_to_move.start)
3312 ..buffer.anchor_before(range_to_move.end),
3313 String::new(),
3314 ));
3315 let insertion_anchor = buffer.anchor_after(insertion_point);
3316 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3317
3318 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3319
3320 // Move selections down
3321 new_selections.extend(contiguous_row_selections.drain(..).map(
3322 |mut selection| {
3323 selection.start.row += row_delta;
3324 selection.end.row += row_delta;
3325 selection
3326 },
3327 ));
3328
3329 // Move folds down
3330 unfold_ranges.push(range_to_move.clone());
3331 for fold in display_map.folds_in_range(
3332 buffer.anchor_before(range_to_move.start)
3333 ..buffer.anchor_after(range_to_move.end),
3334 ) {
3335 let mut start = fold.start.to_point(&buffer);
3336 let mut end = fold.end.to_point(&buffer);
3337 start.row += row_delta;
3338 end.row += row_delta;
3339 refold_ranges.push(start..end);
3340 }
3341 }
3342 }
3343
3344 // If we didn't move line(s), preserve the existing selections
3345 new_selections.extend(contiguous_row_selections.drain(..));
3346 }
3347
3348 self.transact(cx, |this, cx| {
3349 this.unfold_ranges(unfold_ranges, true, cx);
3350 this.buffer.update(cx, |buffer, cx| {
3351 for (range, text) in edits {
3352 buffer.edit([(range, text)], cx);
3353 }
3354 });
3355 this.fold_ranges(refold_ranges, cx);
3356 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3357 });
3358 }
3359
3360 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3361 self.transact(cx, |this, cx| {
3362 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3363 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3364 let line_mode = s.line_mode;
3365 s.move_with(|display_map, selection| {
3366 if !selection.is_empty() || line_mode {
3367 return;
3368 }
3369
3370 let mut head = selection.head();
3371 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3372 if head.column() == display_map.line_len(head.row()) {
3373 transpose_offset = display_map
3374 .buffer_snapshot
3375 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3376 }
3377
3378 if transpose_offset == 0 {
3379 return;
3380 }
3381
3382 *head.column_mut() += 1;
3383 head = display_map.clip_point(head, Bias::Right);
3384 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3385
3386 let transpose_start = display_map
3387 .buffer_snapshot
3388 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3389 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3390 let transpose_end = display_map
3391 .buffer_snapshot
3392 .clip_offset(transpose_offset + 1, Bias::Right);
3393 if let Some(ch) =
3394 display_map.buffer_snapshot.chars_at(transpose_start).next()
3395 {
3396 edits.push((transpose_start..transpose_offset, String::new()));
3397 edits.push((transpose_end..transpose_end, ch.to_string()));
3398 }
3399 }
3400 });
3401 edits
3402 });
3403 this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
3404 let selections = this.selections.all::<usize>(cx);
3405 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3406 s.select(selections);
3407 });
3408 });
3409 }
3410
3411 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3412 let mut text = String::new();
3413 let buffer = self.buffer.read(cx).snapshot(cx);
3414 let mut selections = self.selections.all::<Point>(cx);
3415 let mut clipboard_selections = Vec::with_capacity(selections.len());
3416 {
3417 let max_point = buffer.max_point();
3418 for selection in &mut selections {
3419 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3420 if is_entire_line {
3421 selection.start = Point::new(selection.start.row, 0);
3422 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3423 selection.goal = SelectionGoal::None;
3424 }
3425 let mut len = 0;
3426 for chunk in buffer.text_for_range(selection.start..selection.end) {
3427 text.push_str(chunk);
3428 len += chunk.len();
3429 }
3430 clipboard_selections.push(ClipboardSelection {
3431 len,
3432 is_entire_line,
3433 });
3434 }
3435 }
3436
3437 self.transact(cx, |this, cx| {
3438 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3439 s.select(selections);
3440 });
3441 this.insert("", cx);
3442 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3443 });
3444 }
3445
3446 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3447 let selections = self.selections.all::<Point>(cx);
3448 let buffer = self.buffer.read(cx).read(cx);
3449 let mut text = String::new();
3450
3451 let mut clipboard_selections = Vec::with_capacity(selections.len());
3452 {
3453 let max_point = buffer.max_point();
3454 for selection in selections.iter() {
3455 let mut start = selection.start;
3456 let mut end = selection.end;
3457 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3458 if is_entire_line {
3459 start = Point::new(start.row, 0);
3460 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3461 }
3462 let mut len = 0;
3463 for chunk in buffer.text_for_range(start..end) {
3464 text.push_str(chunk);
3465 len += chunk.len();
3466 }
3467 clipboard_selections.push(ClipboardSelection {
3468 len,
3469 is_entire_line,
3470 });
3471 }
3472 }
3473
3474 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3475 }
3476
3477 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3478 self.transact(cx, |this, cx| {
3479 if let Some(item) = cx.as_mut().read_from_clipboard() {
3480 let mut clipboard_text = Cow::Borrowed(item.text());
3481 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3482 let old_selections = this.selections.all::<usize>(cx);
3483 let all_selections_were_entire_line =
3484 clipboard_selections.iter().all(|s| s.is_entire_line);
3485 if clipboard_selections.len() != old_selections.len() {
3486 let mut newline_separated_text = String::new();
3487 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3488 let mut ix = 0;
3489 while let Some(clipboard_selection) = clipboard_selections.next() {
3490 newline_separated_text
3491 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3492 ix += clipboard_selection.len;
3493 if clipboard_selections.peek().is_some() {
3494 newline_separated_text.push('\n');
3495 }
3496 }
3497 clipboard_text = Cow::Owned(newline_separated_text);
3498 }
3499
3500 this.buffer.update(cx, |buffer, cx| {
3501 let snapshot = buffer.read(cx);
3502 let mut start_offset = 0;
3503 let mut edits = Vec::new();
3504 let line_mode = this.selections.line_mode;
3505 for (ix, selection) in old_selections.iter().enumerate() {
3506 let to_insert;
3507 let entire_line;
3508 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3509 let end_offset = start_offset + clipboard_selection.len;
3510 to_insert = &clipboard_text[start_offset..end_offset];
3511 entire_line = clipboard_selection.is_entire_line;
3512 start_offset = end_offset;
3513 } else {
3514 to_insert = clipboard_text.as_str();
3515 entire_line = all_selections_were_entire_line;
3516 }
3517
3518 // If the corresponding selection was empty when this slice of the
3519 // clipboard text was written, then the entire line containing the
3520 // selection was copied. If this selection is also currently empty,
3521 // then paste the line before the current line of the buffer.
3522 let range = if selection.is_empty() && !line_mode && entire_line {
3523 let column = selection.start.to_point(&snapshot).column as usize;
3524 let line_start = selection.start - column;
3525 line_start..line_start
3526 } else {
3527 selection.range()
3528 };
3529
3530 edits.push((range, to_insert));
3531 }
3532 drop(snapshot);
3533 buffer.edit_with_autoindent(edits, cx);
3534 });
3535
3536 let selections = this.selections.all::<usize>(cx);
3537 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3538 } else {
3539 this.insert(&clipboard_text, cx);
3540 }
3541 }
3542 });
3543 }
3544
3545 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3546 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3547 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3548 self.change_selections(None, cx, |s| {
3549 s.select_anchors(selections.to_vec());
3550 });
3551 }
3552 self.request_autoscroll(Autoscroll::Fit, cx);
3553 cx.emit(Event::Edited);
3554 }
3555 }
3556
3557 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3558 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3559 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3560 {
3561 self.change_selections(None, cx, |s| {
3562 s.select_anchors(selections.to_vec());
3563 });
3564 }
3565 self.request_autoscroll(Autoscroll::Fit, cx);
3566 cx.emit(Event::Edited);
3567 }
3568 }
3569
3570 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3571 self.buffer
3572 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3573 }
3574
3575 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3576 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3577 let line_mode = s.line_mode;
3578 s.move_with(|map, selection| {
3579 let cursor = if selection.is_empty() && !line_mode {
3580 movement::left(map, selection.start)
3581 } else {
3582 selection.start
3583 };
3584 selection.collapse_to(cursor, SelectionGoal::None);
3585 });
3586 })
3587 }
3588
3589 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3590 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3591 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3592 })
3593 }
3594
3595 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3596 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3597 let line_mode = s.line_mode;
3598 s.move_with(|map, selection| {
3599 let cursor = if selection.is_empty() && !line_mode {
3600 movement::right(map, selection.end)
3601 } else {
3602 selection.end
3603 };
3604 selection.collapse_to(cursor, SelectionGoal::None)
3605 });
3606 })
3607 }
3608
3609 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3610 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3611 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3612 })
3613 }
3614
3615 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3616 if self.take_rename(true, cx).is_some() {
3617 return;
3618 }
3619
3620 if let Some(context_menu) = self.context_menu.as_mut() {
3621 if context_menu.select_prev(cx) {
3622 return;
3623 }
3624 }
3625
3626 if matches!(self.mode, EditorMode::SingleLine) {
3627 cx.propagate_action();
3628 return;
3629 }
3630
3631 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3632 let line_mode = s.line_mode;
3633 s.move_with(|map, selection| {
3634 if !selection.is_empty() && !line_mode {
3635 selection.goal = SelectionGoal::None;
3636 }
3637 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3638 selection.collapse_to(cursor, goal);
3639 });
3640 })
3641 }
3642
3643 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3644 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3645 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3646 })
3647 }
3648
3649 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3650 self.take_rename(true, cx);
3651
3652 if let Some(context_menu) = self.context_menu.as_mut() {
3653 if context_menu.select_next(cx) {
3654 return;
3655 }
3656 }
3657
3658 if matches!(self.mode, EditorMode::SingleLine) {
3659 cx.propagate_action();
3660 return;
3661 }
3662
3663 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3664 let line_mode = s.line_mode;
3665 s.move_with(|map, selection| {
3666 if !selection.is_empty() && !line_mode {
3667 selection.goal = SelectionGoal::None;
3668 }
3669 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3670 selection.collapse_to(cursor, goal);
3671 });
3672 });
3673 }
3674
3675 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3676 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3677 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3678 });
3679 }
3680
3681 pub fn move_to_previous_word_start(
3682 &mut self,
3683 _: &MoveToPreviousWordStart,
3684 cx: &mut ViewContext<Self>,
3685 ) {
3686 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3687 s.move_cursors_with(|map, head, _| {
3688 (
3689 movement::previous_word_start(map, head),
3690 SelectionGoal::None,
3691 )
3692 });
3693 })
3694 }
3695
3696 pub fn move_to_previous_subword_start(
3697 &mut self,
3698 _: &MoveToPreviousSubwordStart,
3699 cx: &mut ViewContext<Self>,
3700 ) {
3701 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3702 s.move_cursors_with(|map, head, _| {
3703 (
3704 movement::previous_subword_start(map, head),
3705 SelectionGoal::None,
3706 )
3707 });
3708 })
3709 }
3710
3711 pub fn select_to_previous_word_start(
3712 &mut self,
3713 _: &SelectToPreviousWordStart,
3714 cx: &mut ViewContext<Self>,
3715 ) {
3716 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3717 s.move_heads_with(|map, head, _| {
3718 (
3719 movement::previous_word_start(map, head),
3720 SelectionGoal::None,
3721 )
3722 });
3723 })
3724 }
3725
3726 pub fn select_to_previous_subword_start(
3727 &mut self,
3728 _: &SelectToPreviousSubwordStart,
3729 cx: &mut ViewContext<Self>,
3730 ) {
3731 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3732 s.move_heads_with(|map, head, _| {
3733 (
3734 movement::previous_subword_start(map, head),
3735 SelectionGoal::None,
3736 )
3737 });
3738 })
3739 }
3740
3741 pub fn delete_to_previous_word_start(
3742 &mut self,
3743 _: &DeleteToPreviousWordStart,
3744 cx: &mut ViewContext<Self>,
3745 ) {
3746 self.transact(cx, |this, cx| {
3747 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3748 let line_mode = s.line_mode;
3749 s.move_with(|map, selection| {
3750 if selection.is_empty() && !line_mode {
3751 let cursor = movement::previous_word_start(map, selection.head());
3752 selection.set_head(cursor, SelectionGoal::None);
3753 }
3754 });
3755 });
3756 this.insert("", cx);
3757 });
3758 }
3759
3760 pub fn delete_to_previous_subword_start(
3761 &mut self,
3762 _: &DeleteToPreviousSubwordStart,
3763 cx: &mut ViewContext<Self>,
3764 ) {
3765 self.transact(cx, |this, cx| {
3766 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3767 let line_mode = s.line_mode;
3768 s.move_with(|map, selection| {
3769 if selection.is_empty() && !line_mode {
3770 let cursor = movement::previous_subword_start(map, selection.head());
3771 selection.set_head(cursor, SelectionGoal::None);
3772 }
3773 });
3774 });
3775 this.insert("", cx);
3776 });
3777 }
3778
3779 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3780 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3781 s.move_cursors_with(|map, head, _| {
3782 (movement::next_word_end(map, head), SelectionGoal::None)
3783 });
3784 })
3785 }
3786
3787 pub fn move_to_next_subword_end(
3788 &mut self,
3789 _: &MoveToNextSubwordEnd,
3790 cx: &mut ViewContext<Self>,
3791 ) {
3792 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3793 s.move_cursors_with(|map, head, _| {
3794 (movement::next_subword_end(map, head), SelectionGoal::None)
3795 });
3796 })
3797 }
3798
3799 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3800 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3801 s.move_heads_with(|map, head, _| {
3802 (movement::next_word_end(map, head), SelectionGoal::None)
3803 });
3804 })
3805 }
3806
3807 pub fn select_to_next_subword_end(
3808 &mut self,
3809 _: &SelectToNextSubwordEnd,
3810 cx: &mut ViewContext<Self>,
3811 ) {
3812 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3813 s.move_heads_with(|map, head, _| {
3814 (movement::next_subword_end(map, head), SelectionGoal::None)
3815 });
3816 })
3817 }
3818
3819 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3820 self.transact(cx, |this, cx| {
3821 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3822 let line_mode = s.line_mode;
3823 s.move_with(|map, selection| {
3824 if selection.is_empty() && !line_mode {
3825 let cursor = movement::next_word_end(map, selection.head());
3826 selection.set_head(cursor, SelectionGoal::None);
3827 }
3828 });
3829 });
3830 this.insert("", cx);
3831 });
3832 }
3833
3834 pub fn delete_to_next_subword_end(
3835 &mut self,
3836 _: &DeleteToNextSubwordEnd,
3837 cx: &mut ViewContext<Self>,
3838 ) {
3839 self.transact(cx, |this, cx| {
3840 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3841 s.move_with(|map, selection| {
3842 if selection.is_empty() {
3843 let cursor = movement::next_subword_end(map, selection.head());
3844 selection.set_head(cursor, SelectionGoal::None);
3845 }
3846 });
3847 });
3848 this.insert("", cx);
3849 });
3850 }
3851
3852 pub fn move_to_beginning_of_line(
3853 &mut self,
3854 _: &MoveToBeginningOfLine,
3855 cx: &mut ViewContext<Self>,
3856 ) {
3857 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3858 s.move_cursors_with(|map, head, _| {
3859 (
3860 movement::line_beginning(map, head, true),
3861 SelectionGoal::None,
3862 )
3863 });
3864 })
3865 }
3866
3867 pub fn select_to_beginning_of_line(
3868 &mut self,
3869 action: &SelectToBeginningOfLine,
3870 cx: &mut ViewContext<Self>,
3871 ) {
3872 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3873 s.move_heads_with(|map, head, _| {
3874 (
3875 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3876 SelectionGoal::None,
3877 )
3878 });
3879 });
3880 }
3881
3882 pub fn delete_to_beginning_of_line(
3883 &mut self,
3884 _: &DeleteToBeginningOfLine,
3885 cx: &mut ViewContext<Self>,
3886 ) {
3887 self.transact(cx, |this, cx| {
3888 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3889 s.move_with(|_, selection| {
3890 selection.reversed = true;
3891 });
3892 });
3893
3894 this.select_to_beginning_of_line(
3895 &SelectToBeginningOfLine {
3896 stop_at_soft_wraps: false,
3897 },
3898 cx,
3899 );
3900 this.backspace(&Backspace, cx);
3901 });
3902 }
3903
3904 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
3905 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3906 s.move_cursors_with(|map, head, _| {
3907 (movement::line_end(map, head, true), SelectionGoal::None)
3908 });
3909 })
3910 }
3911
3912 pub fn select_to_end_of_line(
3913 &mut self,
3914 action: &SelectToEndOfLine,
3915 cx: &mut ViewContext<Self>,
3916 ) {
3917 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3918 s.move_heads_with(|map, head, _| {
3919 (
3920 movement::line_end(map, head, action.stop_at_soft_wraps),
3921 SelectionGoal::None,
3922 )
3923 });
3924 })
3925 }
3926
3927 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
3928 self.transact(cx, |this, cx| {
3929 this.select_to_end_of_line(
3930 &SelectToEndOfLine {
3931 stop_at_soft_wraps: false,
3932 },
3933 cx,
3934 );
3935 this.delete(&Delete, cx);
3936 });
3937 }
3938
3939 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
3940 self.transact(cx, |this, cx| {
3941 this.select_to_end_of_line(
3942 &SelectToEndOfLine {
3943 stop_at_soft_wraps: false,
3944 },
3945 cx,
3946 );
3947 this.cut(&Cut, cx);
3948 });
3949 }
3950
3951 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
3952 if matches!(self.mode, EditorMode::SingleLine) {
3953 cx.propagate_action();
3954 return;
3955 }
3956
3957 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3958 s.select_ranges(vec![0..0]);
3959 });
3960 }
3961
3962 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
3963 let mut selection = self.selections.last::<Point>(cx);
3964 selection.set_head(Point::zero(), SelectionGoal::None);
3965
3966 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3967 s.select(vec![selection]);
3968 });
3969 }
3970
3971 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
3972 if matches!(self.mode, EditorMode::SingleLine) {
3973 cx.propagate_action();
3974 return;
3975 }
3976
3977 let cursor = self.buffer.read(cx).read(cx).len();
3978 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3979 s.select_ranges(vec![cursor..cursor])
3980 });
3981 }
3982
3983 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
3984 self.nav_history = nav_history;
3985 }
3986
3987 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
3988 self.nav_history.as_ref()
3989 }
3990
3991 fn push_to_nav_history(
3992 &self,
3993 position: Anchor,
3994 new_position: Option<Point>,
3995 cx: &mut ViewContext<Self>,
3996 ) {
3997 if let Some(nav_history) = &self.nav_history {
3998 let buffer = self.buffer.read(cx).read(cx);
3999 let point = position.to_point(&buffer);
4000 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4001 drop(buffer);
4002
4003 if let Some(new_position) = new_position {
4004 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4005 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4006 return;
4007 }
4008 }
4009
4010 nav_history.push(Some(NavigationData {
4011 cursor_anchor: position,
4012 cursor_position: point,
4013 scroll_position: self.scroll_position,
4014 scroll_top_anchor: self.scroll_top_anchor.clone(),
4015 scroll_top_row,
4016 }));
4017 }
4018 }
4019
4020 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4021 let buffer = self.buffer.read(cx).snapshot(cx);
4022 let mut selection = self.selections.first::<usize>(cx);
4023 selection.set_head(buffer.len(), SelectionGoal::None);
4024 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4025 s.select(vec![selection]);
4026 });
4027 }
4028
4029 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4030 let end = self.buffer.read(cx).read(cx).len();
4031 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4032 s.select_ranges(vec![0..end]);
4033 });
4034 }
4035
4036 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4037 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4038 let mut selections = self.selections.all::<Point>(cx);
4039 let max_point = display_map.buffer_snapshot.max_point();
4040 for selection in &mut selections {
4041 let rows = selection.spanned_rows(true, &display_map);
4042 selection.start = Point::new(rows.start, 0);
4043 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4044 selection.reversed = false;
4045 }
4046 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4047 s.select(selections);
4048 });
4049 }
4050
4051 pub fn split_selection_into_lines(
4052 &mut self,
4053 _: &SplitSelectionIntoLines,
4054 cx: &mut ViewContext<Self>,
4055 ) {
4056 let mut to_unfold = Vec::new();
4057 let mut new_selection_ranges = Vec::new();
4058 {
4059 let selections = self.selections.all::<Point>(cx);
4060 let buffer = self.buffer.read(cx).read(cx);
4061 for selection in selections {
4062 for row in selection.start.row..selection.end.row {
4063 let cursor = Point::new(row, buffer.line_len(row));
4064 new_selection_ranges.push(cursor..cursor);
4065 }
4066 new_selection_ranges.push(selection.end..selection.end);
4067 to_unfold.push(selection.start..selection.end);
4068 }
4069 }
4070 self.unfold_ranges(to_unfold, true, cx);
4071 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4072 s.select_ranges(new_selection_ranges);
4073 });
4074 }
4075
4076 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4077 self.add_selection(true, cx);
4078 }
4079
4080 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4081 self.add_selection(false, cx);
4082 }
4083
4084 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4085 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4086 let mut selections = self.selections.all::<Point>(cx);
4087 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4088 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4089 let range = oldest_selection.display_range(&display_map).sorted();
4090 let columns = cmp::min(range.start.column(), range.end.column())
4091 ..cmp::max(range.start.column(), range.end.column());
4092
4093 selections.clear();
4094 let mut stack = Vec::new();
4095 for row in range.start.row()..=range.end.row() {
4096 if let Some(selection) = self.selections.build_columnar_selection(
4097 &display_map,
4098 row,
4099 &columns,
4100 oldest_selection.reversed,
4101 ) {
4102 stack.push(selection.id);
4103 selections.push(selection);
4104 }
4105 }
4106
4107 if above {
4108 stack.reverse();
4109 }
4110
4111 AddSelectionsState { above, stack }
4112 });
4113
4114 let last_added_selection = *state.stack.last().unwrap();
4115 let mut new_selections = Vec::new();
4116 if above == state.above {
4117 let end_row = if above {
4118 0
4119 } else {
4120 display_map.max_point().row()
4121 };
4122
4123 'outer: for selection in selections {
4124 if selection.id == last_added_selection {
4125 let range = selection.display_range(&display_map).sorted();
4126 debug_assert_eq!(range.start.row(), range.end.row());
4127 let mut row = range.start.row();
4128 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4129 {
4130 start..end
4131 } else {
4132 cmp::min(range.start.column(), range.end.column())
4133 ..cmp::max(range.start.column(), range.end.column())
4134 };
4135
4136 while row != end_row {
4137 if above {
4138 row -= 1;
4139 } else {
4140 row += 1;
4141 }
4142
4143 if let Some(new_selection) = self.selections.build_columnar_selection(
4144 &display_map,
4145 row,
4146 &columns,
4147 selection.reversed,
4148 ) {
4149 state.stack.push(new_selection.id);
4150 if above {
4151 new_selections.push(new_selection);
4152 new_selections.push(selection);
4153 } else {
4154 new_selections.push(selection);
4155 new_selections.push(new_selection);
4156 }
4157
4158 continue 'outer;
4159 }
4160 }
4161 }
4162
4163 new_selections.push(selection);
4164 }
4165 } else {
4166 new_selections = selections;
4167 new_selections.retain(|s| s.id != last_added_selection);
4168 state.stack.pop();
4169 }
4170
4171 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4172 s.select(new_selections);
4173 });
4174 if state.stack.len() > 1 {
4175 self.add_selections_state = Some(state);
4176 }
4177 }
4178
4179 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4180 self.push_to_selection_history();
4181 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4182 let buffer = &display_map.buffer_snapshot;
4183 let mut selections = self.selections.all::<usize>(cx);
4184 if let Some(mut select_next_state) = self.select_next_state.take() {
4185 let query = &select_next_state.query;
4186 if !select_next_state.done {
4187 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4188 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4189 let mut next_selected_range = None;
4190
4191 let bytes_after_last_selection =
4192 buffer.bytes_in_range(last_selection.end..buffer.len());
4193 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4194 let query_matches = query
4195 .stream_find_iter(bytes_after_last_selection)
4196 .map(|result| (last_selection.end, result))
4197 .chain(
4198 query
4199 .stream_find_iter(bytes_before_first_selection)
4200 .map(|result| (0, result)),
4201 );
4202 for (start_offset, query_match) in query_matches {
4203 let query_match = query_match.unwrap(); // can only fail due to I/O
4204 let offset_range =
4205 start_offset + query_match.start()..start_offset + query_match.end();
4206 let display_range = offset_range.start.to_display_point(&display_map)
4207 ..offset_range.end.to_display_point(&display_map);
4208
4209 if !select_next_state.wordwise
4210 || (!movement::is_inside_word(&display_map, display_range.start)
4211 && !movement::is_inside_word(&display_map, display_range.end))
4212 {
4213 next_selected_range = Some(offset_range);
4214 break;
4215 }
4216 }
4217
4218 if let Some(next_selected_range) = next_selected_range {
4219 self.unfold_ranges([next_selected_range.clone()], false, cx);
4220 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4221 if action.replace_newest {
4222 s.delete(s.newest_anchor().id);
4223 }
4224 s.insert_range(next_selected_range);
4225 });
4226 } else {
4227 select_next_state.done = true;
4228 }
4229 }
4230
4231 self.select_next_state = Some(select_next_state);
4232 } else if selections.len() == 1 {
4233 let selection = selections.last_mut().unwrap();
4234 if selection.start == selection.end {
4235 let word_range = movement::surrounding_word(
4236 &display_map,
4237 selection.start.to_display_point(&display_map),
4238 );
4239 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4240 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4241 selection.goal = SelectionGoal::None;
4242 selection.reversed = false;
4243
4244 let query = buffer
4245 .text_for_range(selection.start..selection.end)
4246 .collect::<String>();
4247 let select_state = SelectNextState {
4248 query: AhoCorasick::new_auto_configured(&[query]),
4249 wordwise: true,
4250 done: false,
4251 };
4252 self.unfold_ranges([selection.start..selection.end], false, cx);
4253 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4254 s.select(selections);
4255 });
4256 self.select_next_state = Some(select_state);
4257 } else {
4258 let query = buffer
4259 .text_for_range(selection.start..selection.end)
4260 .collect::<String>();
4261 self.select_next_state = Some(SelectNextState {
4262 query: AhoCorasick::new_auto_configured(&[query]),
4263 wordwise: false,
4264 done: false,
4265 });
4266 self.select_next(action, cx);
4267 }
4268 }
4269 }
4270
4271 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4272 self.transact(cx, |this, cx| {
4273 let mut selections = this.selections.all::<Point>(cx);
4274 let mut all_selection_lines_are_comments = true;
4275 let mut edit_ranges = Vec::new();
4276 let mut last_toggled_row = None;
4277 this.buffer.update(cx, |buffer, cx| {
4278 // TODO: Handle selections that cross excerpts
4279 for selection in &mut selections {
4280 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4281 // as that portion won't be used for detecting if a line is a comment.
4282 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4283 .language_at(selection.start, cx)
4284 .and_then(|l| l.line_comment_prefix())
4285 {
4286 prefix.into()
4287 } else {
4288 return;
4289 };
4290 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4291 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4292 edit_ranges.clear();
4293 let snapshot = buffer.snapshot(cx);
4294
4295 let end_row =
4296 if selection.end.row > selection.start.row && selection.end.column == 0 {
4297 selection.end.row
4298 } else {
4299 selection.end.row + 1
4300 };
4301
4302 for row in selection.start.row..end_row {
4303 // If multiple selections contain a given row, avoid processing that
4304 // row more than once.
4305 if last_toggled_row == Some(row) {
4306 continue;
4307 } else {
4308 last_toggled_row = Some(row);
4309 }
4310
4311 if snapshot.is_line_blank(row) {
4312 continue;
4313 }
4314
4315 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4316 let mut line_bytes = snapshot
4317 .bytes_in_range(start..snapshot.max_point())
4318 .flatten()
4319 .copied();
4320
4321 // If this line currently begins with the line comment prefix, then record
4322 // the range containing the prefix.
4323 if all_selection_lines_are_comments
4324 && line_bytes
4325 .by_ref()
4326 .take(comment_prefix.len())
4327 .eq(comment_prefix.bytes())
4328 {
4329 // Include any whitespace that matches the comment prefix.
4330 let matching_whitespace_len = line_bytes
4331 .zip(comment_prefix_whitespace.bytes())
4332 .take_while(|(a, b)| a == b)
4333 .count()
4334 as u32;
4335 let end = Point::new(
4336 row,
4337 start.column
4338 + comment_prefix.len() as u32
4339 + matching_whitespace_len,
4340 );
4341 edit_ranges.push(start..end);
4342 }
4343 // If this line does not begin with the line comment prefix, then record
4344 // the position where the prefix should be inserted.
4345 else {
4346 all_selection_lines_are_comments = false;
4347 edit_ranges.push(start..start);
4348 }
4349 }
4350
4351 if !edit_ranges.is_empty() {
4352 if all_selection_lines_are_comments {
4353 let empty_str: Arc<str> = "".into();
4354 buffer.edit(
4355 edit_ranges
4356 .iter()
4357 .cloned()
4358 .map(|range| (range, empty_str.clone())),
4359 cx,
4360 );
4361 } else {
4362 let min_column =
4363 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4364 let edits = edit_ranges.iter().map(|range| {
4365 let position = Point::new(range.start.row, min_column);
4366 (position..position, full_comment_prefix.clone())
4367 });
4368 buffer.edit(edits, cx);
4369 }
4370 }
4371 }
4372 });
4373
4374 let selections = this.selections.all::<usize>(cx);
4375 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4376 });
4377 }
4378
4379 pub fn select_larger_syntax_node(
4380 &mut self,
4381 _: &SelectLargerSyntaxNode,
4382 cx: &mut ViewContext<Self>,
4383 ) {
4384 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4385 let buffer = self.buffer.read(cx).snapshot(cx);
4386 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4387
4388 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4389 let mut selected_larger_node = false;
4390 let new_selections = old_selections
4391 .iter()
4392 .map(|selection| {
4393 let old_range = selection.start..selection.end;
4394 let mut new_range = old_range.clone();
4395 while let Some(containing_range) =
4396 buffer.range_for_syntax_ancestor(new_range.clone())
4397 {
4398 new_range = containing_range;
4399 if !display_map.intersects_fold(new_range.start)
4400 && !display_map.intersects_fold(new_range.end)
4401 {
4402 break;
4403 }
4404 }
4405
4406 selected_larger_node |= new_range != old_range;
4407 Selection {
4408 id: selection.id,
4409 start: new_range.start,
4410 end: new_range.end,
4411 goal: SelectionGoal::None,
4412 reversed: selection.reversed,
4413 }
4414 })
4415 .collect::<Vec<_>>();
4416
4417 if selected_larger_node {
4418 stack.push(old_selections);
4419 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4420 s.select(new_selections);
4421 });
4422 }
4423 self.select_larger_syntax_node_stack = stack;
4424 }
4425
4426 pub fn select_smaller_syntax_node(
4427 &mut self,
4428 _: &SelectSmallerSyntaxNode,
4429 cx: &mut ViewContext<Self>,
4430 ) {
4431 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4432 if let Some(selections) = stack.pop() {
4433 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4434 s.select(selections.to_vec());
4435 });
4436 }
4437 self.select_larger_syntax_node_stack = stack;
4438 }
4439
4440 pub fn move_to_enclosing_bracket(
4441 &mut self,
4442 _: &MoveToEnclosingBracket,
4443 cx: &mut ViewContext<Self>,
4444 ) {
4445 let buffer = self.buffer.read(cx).snapshot(cx);
4446 let mut selections = self.selections.all::<usize>(cx);
4447 for selection in &mut selections {
4448 if let Some((open_range, close_range)) =
4449 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4450 {
4451 let close_range = close_range.to_inclusive();
4452 let destination = if close_range.contains(&selection.start)
4453 && close_range.contains(&selection.end)
4454 {
4455 open_range.end
4456 } else {
4457 *close_range.start()
4458 };
4459 selection.start = destination;
4460 selection.end = destination;
4461 }
4462 }
4463
4464 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4465 s.select(selections);
4466 });
4467 }
4468
4469 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4470 self.end_selection(cx);
4471 self.selection_history.mode = SelectionHistoryMode::Undoing;
4472 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4473 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4474 self.select_next_state = entry.select_next_state;
4475 self.add_selections_state = entry.add_selections_state;
4476 self.request_autoscroll(Autoscroll::Newest, cx);
4477 }
4478 self.selection_history.mode = SelectionHistoryMode::Normal;
4479 }
4480
4481 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4482 self.end_selection(cx);
4483 self.selection_history.mode = SelectionHistoryMode::Redoing;
4484 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4485 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4486 self.select_next_state = entry.select_next_state;
4487 self.add_selections_state = entry.add_selections_state;
4488 self.request_autoscroll(Autoscroll::Newest, cx);
4489 }
4490 self.selection_history.mode = SelectionHistoryMode::Normal;
4491 }
4492
4493 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4494 self.go_to_diagnostic(Direction::Next, cx)
4495 }
4496
4497 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4498 self.go_to_diagnostic(Direction::Prev, cx)
4499 }
4500
4501 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4502 let buffer = self.buffer.read(cx).snapshot(cx);
4503 let selection = self.selections.newest::<usize>(cx);
4504 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4505 active_diagnostics
4506 .primary_range
4507 .to_offset(&buffer)
4508 .to_inclusive()
4509 });
4510 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4511 if active_primary_range.contains(&selection.head()) {
4512 *active_primary_range.end()
4513 } else {
4514 selection.head()
4515 }
4516 } else {
4517 selection.head()
4518 };
4519
4520 loop {
4521 let mut diagnostics = if direction == Direction::Prev {
4522 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4523 } else {
4524 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4525 };
4526 let group = diagnostics.find_map(|entry| {
4527 if entry.diagnostic.is_primary
4528 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4529 && !entry.range.is_empty()
4530 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4531 {
4532 Some((entry.range, entry.diagnostic.group_id))
4533 } else {
4534 None
4535 }
4536 });
4537
4538 if let Some((primary_range, group_id)) = group {
4539 self.activate_diagnostics(group_id, cx);
4540 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4541 s.select(vec![Selection {
4542 id: selection.id,
4543 start: primary_range.start,
4544 end: primary_range.start,
4545 reversed: false,
4546 goal: SelectionGoal::None,
4547 }]);
4548 });
4549 break;
4550 } else {
4551 // Cycle around to the start of the buffer, potentially moving back to the start of
4552 // the currently active diagnostic.
4553 active_primary_range.take();
4554 if direction == Direction::Prev {
4555 if search_start == buffer.len() {
4556 break;
4557 } else {
4558 search_start = buffer.len();
4559 }
4560 } else {
4561 if search_start == 0 {
4562 break;
4563 } else {
4564 search_start = 0;
4565 }
4566 }
4567 }
4568 }
4569 }
4570
4571 pub fn go_to_definition(
4572 workspace: &mut Workspace,
4573 _: &GoToDefinition,
4574 cx: &mut ViewContext<Workspace>,
4575 ) {
4576 let active_item = workspace.active_item(cx);
4577 let editor_handle = if let Some(editor) = active_item
4578 .as_ref()
4579 .and_then(|item| item.act_as::<Self>(cx))
4580 {
4581 editor
4582 } else {
4583 return;
4584 };
4585
4586 let editor = editor_handle.read(cx);
4587 let buffer = editor.buffer.read(cx);
4588 let head = editor.selections.newest::<usize>(cx).head();
4589 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4590 text_anchor
4591 } else {
4592 return;
4593 };
4594
4595 let project = workspace.project().clone();
4596 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4597 cx.spawn(|workspace, mut cx| async move {
4598 let definitions = definitions.await?;
4599 workspace.update(&mut cx, |workspace, cx| {
4600 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4601 for definition in definitions {
4602 let range = definition.range.to_offset(definition.buffer.read(cx));
4603
4604 let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
4605 target_editor_handle.update(cx, |target_editor, cx| {
4606 // When selecting a definition in a different buffer, disable the nav history
4607 // to avoid creating a history entry at the previous cursor location.
4608 if editor_handle != target_editor_handle {
4609 nav_history.borrow_mut().disable();
4610 }
4611 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4612 s.select_ranges([range]);
4613 });
4614
4615 nav_history.borrow_mut().enable();
4616 });
4617 }
4618 });
4619
4620 Ok::<(), anyhow::Error>(())
4621 })
4622 .detach_and_log_err(cx);
4623 }
4624
4625 pub fn find_all_references(
4626 workspace: &mut Workspace,
4627 _: &FindAllReferences,
4628 cx: &mut ViewContext<Workspace>,
4629 ) -> Option<Task<Result<()>>> {
4630 let active_item = workspace.active_item(cx)?;
4631 let editor_handle = active_item.act_as::<Self>(cx)?;
4632
4633 let editor = editor_handle.read(cx);
4634 let buffer = editor.buffer.read(cx);
4635 let head = editor.selections.newest::<usize>(cx).head();
4636 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4637 let replica_id = editor.replica_id(cx);
4638
4639 let project = workspace.project().clone();
4640 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4641 Some(cx.spawn(|workspace, mut cx| async move {
4642 let mut locations = references.await?;
4643 if locations.is_empty() {
4644 return Ok(());
4645 }
4646
4647 locations.sort_by_key(|location| location.buffer.id());
4648 let mut locations = locations.into_iter().peekable();
4649 let mut ranges_to_highlight = Vec::new();
4650
4651 let excerpt_buffer = cx.add_model(|cx| {
4652 let mut symbol_name = None;
4653 let mut multibuffer = MultiBuffer::new(replica_id);
4654 while let Some(location) = locations.next() {
4655 let buffer = location.buffer.read(cx);
4656 let mut ranges_for_buffer = Vec::new();
4657 let range = location.range.to_offset(buffer);
4658 ranges_for_buffer.push(range.clone());
4659 if symbol_name.is_none() {
4660 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4661 }
4662
4663 while let Some(next_location) = locations.peek() {
4664 if next_location.buffer == location.buffer {
4665 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4666 locations.next();
4667 } else {
4668 break;
4669 }
4670 }
4671
4672 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4673 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4674 location.buffer.clone(),
4675 ranges_for_buffer,
4676 1,
4677 cx,
4678 ));
4679 }
4680 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4681 });
4682
4683 workspace.update(&mut cx, |workspace, cx| {
4684 let editor =
4685 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4686 editor.update(cx, |editor, cx| {
4687 editor.highlight_background::<Self>(
4688 ranges_to_highlight,
4689 |theme| theme.editor.highlighted_line_background,
4690 cx,
4691 );
4692 });
4693 workspace.add_item(Box::new(editor), cx);
4694 });
4695
4696 Ok(())
4697 }))
4698 }
4699
4700 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4701 use language::ToOffset as _;
4702
4703 let project = self.project.clone()?;
4704 let selection = self.selections.newest_anchor().clone();
4705 let (cursor_buffer, cursor_buffer_position) = self
4706 .buffer
4707 .read(cx)
4708 .text_anchor_for_position(selection.head(), cx)?;
4709 let (tail_buffer, _) = self
4710 .buffer
4711 .read(cx)
4712 .text_anchor_for_position(selection.tail(), cx)?;
4713 if tail_buffer != cursor_buffer {
4714 return None;
4715 }
4716
4717 let snapshot = cursor_buffer.read(cx).snapshot();
4718 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4719 let prepare_rename = project.update(cx, |project, cx| {
4720 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4721 });
4722
4723 Some(cx.spawn(|this, mut cx| async move {
4724 let rename_range = if let Some(range) = prepare_rename.await? {
4725 Some(range)
4726 } else {
4727 this.read_with(&cx, |this, cx| {
4728 let buffer = this.buffer.read(cx).snapshot(cx);
4729 let mut buffer_highlights = this
4730 .document_highlights_for_position(selection.head(), &buffer)
4731 .filter(|highlight| {
4732 highlight.start.excerpt_id() == selection.head().excerpt_id()
4733 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4734 });
4735 buffer_highlights
4736 .next()
4737 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4738 })
4739 };
4740 if let Some(rename_range) = rename_range {
4741 let rename_buffer_range = rename_range.to_offset(&snapshot);
4742 let cursor_offset_in_rename_range =
4743 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4744
4745 this.update(&mut cx, |this, cx| {
4746 this.take_rename(false, cx);
4747 let style = this.style(cx);
4748 let buffer = this.buffer.read(cx).read(cx);
4749 let cursor_offset = selection.head().to_offset(&buffer);
4750 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4751 let rename_end = rename_start + rename_buffer_range.len();
4752 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4753 let mut old_highlight_id = None;
4754 let old_name: Arc<str> = buffer
4755 .chunks(rename_start..rename_end, true)
4756 .map(|chunk| {
4757 if old_highlight_id.is_none() {
4758 old_highlight_id = chunk.syntax_highlight_id;
4759 }
4760 chunk.text
4761 })
4762 .collect::<String>()
4763 .into();
4764
4765 drop(buffer);
4766
4767 // Position the selection in the rename editor so that it matches the current selection.
4768 this.show_local_selections = false;
4769 let rename_editor = cx.add_view(|cx| {
4770 let mut editor = Editor::single_line(None, cx);
4771 if let Some(old_highlight_id) = old_highlight_id {
4772 editor.override_text_style =
4773 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4774 }
4775 editor
4776 .buffer
4777 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4778 editor.select_all(&SelectAll, cx);
4779 editor
4780 });
4781
4782 let ranges = this
4783 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4784 .into_iter()
4785 .flat_map(|(_, ranges)| ranges)
4786 .chain(
4787 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4788 .into_iter()
4789 .flat_map(|(_, ranges)| ranges),
4790 )
4791 .collect();
4792
4793 this.highlight_text::<Rename>(
4794 ranges,
4795 HighlightStyle {
4796 fade_out: Some(style.rename_fade),
4797 ..Default::default()
4798 },
4799 cx,
4800 );
4801 cx.focus(&rename_editor);
4802 let block_id = this.insert_blocks(
4803 [BlockProperties {
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 position: buffer.anchor_after(entry.range.start),
4989 height: message_height,
4990 render: diagnostic_block_renderer(diagnostic, true),
4991 disposition: BlockDisposition::Below,
4992 }
4993 }),
4994 cx,
4995 )
4996 .into_iter()
4997 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
4998 .collect();
4999
5000 Some(ActiveDiagnosticGroup {
5001 primary_range,
5002 primary_message,
5003 blocks,
5004 is_valid: true,
5005 })
5006 });
5007 }
5008
5009 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5010 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5011 self.display_map.update(cx, |display_map, cx| {
5012 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5013 });
5014 cx.notify();
5015 }
5016 }
5017
5018 pub fn set_selections_from_remote(
5019 &mut self,
5020 selections: Vec<Selection<Anchor>>,
5021 cx: &mut ViewContext<Self>,
5022 ) {
5023 let old_cursor_position = self.selections.newest_anchor().head();
5024 self.selections.change_with(cx, |s| {
5025 s.select_anchors(selections);
5026 });
5027 self.selections_did_change(false, &old_cursor_position, cx);
5028 }
5029
5030 fn push_to_selection_history(&mut self) {
5031 self.selection_history.push(SelectionHistoryEntry {
5032 selections: self.selections.disjoint_anchors().clone(),
5033 select_next_state: self.select_next_state.clone(),
5034 add_selections_state: self.add_selections_state.clone(),
5035 });
5036 }
5037
5038 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5039 self.autoscroll_request = Some((autoscroll, true));
5040 cx.notify();
5041 }
5042
5043 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5044 self.autoscroll_request = Some((autoscroll, false));
5045 cx.notify();
5046 }
5047
5048 pub fn transact(
5049 &mut self,
5050 cx: &mut ViewContext<Self>,
5051 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5052 ) {
5053 self.start_transaction_at(Instant::now(), cx);
5054 update(self, cx);
5055 self.end_transaction_at(Instant::now(), cx);
5056 }
5057
5058 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5059 self.end_selection(cx);
5060 if let Some(tx_id) = self
5061 .buffer
5062 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5063 {
5064 self.selection_history
5065 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5066 }
5067 }
5068
5069 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5070 if let Some(tx_id) = self
5071 .buffer
5072 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5073 {
5074 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5075 *end_selections = Some(self.selections.disjoint_anchors().clone());
5076 } else {
5077 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5078 }
5079
5080 cx.emit(Event::Edited);
5081 }
5082 }
5083
5084 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5085 log::info!("Editor::page_up");
5086 }
5087
5088 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5089 log::info!("Editor::page_down");
5090 }
5091
5092 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5093 let mut fold_ranges = Vec::new();
5094
5095 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5096 let selections = self.selections.all::<Point>(cx);
5097 for selection in selections {
5098 let range = selection.display_range(&display_map).sorted();
5099 let buffer_start_row = range.start.to_point(&display_map).row;
5100
5101 for row in (0..=range.end.row()).rev() {
5102 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5103 let fold_range = self.foldable_range_for_line(&display_map, row);
5104 if fold_range.end.row >= buffer_start_row {
5105 fold_ranges.push(fold_range);
5106 if row <= range.start.row() {
5107 break;
5108 }
5109 }
5110 }
5111 }
5112 }
5113
5114 self.fold_ranges(fold_ranges, cx);
5115 }
5116
5117 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5118 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5119 let buffer = &display_map.buffer_snapshot;
5120 let selections = self.selections.all::<Point>(cx);
5121 let ranges = selections
5122 .iter()
5123 .map(|s| {
5124 let range = s.display_range(&display_map).sorted();
5125 let mut start = range.start.to_point(&display_map);
5126 let mut end = range.end.to_point(&display_map);
5127 start.column = 0;
5128 end.column = buffer.line_len(end.row);
5129 start..end
5130 })
5131 .collect::<Vec<_>>();
5132 self.unfold_ranges(ranges, true, cx);
5133 }
5134
5135 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5136 let max_point = display_map.max_point();
5137 if display_row >= max_point.row() {
5138 false
5139 } else {
5140 let (start_indent, is_blank) = display_map.line_indent(display_row);
5141 if is_blank {
5142 false
5143 } else {
5144 for display_row in display_row + 1..=max_point.row() {
5145 let (indent, is_blank) = display_map.line_indent(display_row);
5146 if !is_blank {
5147 return indent > start_indent;
5148 }
5149 }
5150 false
5151 }
5152 }
5153 }
5154
5155 fn foldable_range_for_line(
5156 &self,
5157 display_map: &DisplaySnapshot,
5158 start_row: u32,
5159 ) -> Range<Point> {
5160 let max_point = display_map.max_point();
5161
5162 let (start_indent, _) = display_map.line_indent(start_row);
5163 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5164 let mut end = None;
5165 for row in start_row + 1..=max_point.row() {
5166 let (indent, is_blank) = display_map.line_indent(row);
5167 if !is_blank && indent <= start_indent {
5168 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5169 break;
5170 }
5171 }
5172
5173 let end = end.unwrap_or(max_point);
5174 return start.to_point(display_map)..end.to_point(display_map);
5175 }
5176
5177 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5178 let selections = self.selections.all::<Point>(cx);
5179 let ranges = selections.into_iter().map(|s| s.start..s.end);
5180 self.fold_ranges(ranges, cx);
5181 }
5182
5183 pub fn fold_ranges<T: ToOffset>(
5184 &mut self,
5185 ranges: impl IntoIterator<Item = Range<T>>,
5186 cx: &mut ViewContext<Self>,
5187 ) {
5188 let mut ranges = ranges.into_iter().peekable();
5189 if ranges.peek().is_some() {
5190 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5191 self.request_autoscroll(Autoscroll::Fit, cx);
5192 cx.notify();
5193 }
5194 }
5195
5196 pub fn unfold_ranges<T: ToOffset>(
5197 &mut self,
5198 ranges: impl IntoIterator<Item = Range<T>>,
5199 inclusive: bool,
5200 cx: &mut ViewContext<Self>,
5201 ) {
5202 let mut ranges = ranges.into_iter().peekable();
5203 if ranges.peek().is_some() {
5204 self.display_map
5205 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5206 self.request_autoscroll(Autoscroll::Fit, cx);
5207 cx.notify();
5208 }
5209 }
5210
5211 pub fn insert_blocks(
5212 &mut self,
5213 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5214 cx: &mut ViewContext<Self>,
5215 ) -> Vec<BlockId> {
5216 let blocks = self
5217 .display_map
5218 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5219 self.request_autoscroll(Autoscroll::Fit, cx);
5220 blocks
5221 }
5222
5223 pub fn replace_blocks(
5224 &mut self,
5225 blocks: HashMap<BlockId, RenderBlock>,
5226 cx: &mut ViewContext<Self>,
5227 ) {
5228 self.display_map
5229 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5230 self.request_autoscroll(Autoscroll::Fit, cx);
5231 }
5232
5233 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5234 self.display_map.update(cx, |display_map, cx| {
5235 display_map.remove_blocks(block_ids, cx)
5236 });
5237 }
5238
5239 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5240 self.display_map
5241 .update(cx, |map, cx| map.snapshot(cx))
5242 .longest_row()
5243 }
5244
5245 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5246 self.display_map
5247 .update(cx, |map, cx| map.snapshot(cx))
5248 .max_point()
5249 }
5250
5251 pub fn text(&self, cx: &AppContext) -> String {
5252 self.buffer.read(cx).read(cx).text()
5253 }
5254
5255 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5256 self.transact(cx, |this, cx| {
5257 this.buffer
5258 .read(cx)
5259 .as_singleton()
5260 .expect("you can only call set_text on editors for singleton buffers")
5261 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5262 });
5263 }
5264
5265 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5266 self.display_map
5267 .update(cx, |map, cx| map.snapshot(cx))
5268 .text()
5269 }
5270
5271 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5272 let language_name = self
5273 .buffer
5274 .read(cx)
5275 .as_singleton()
5276 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5277 .map(|l| l.name());
5278
5279 let settings = cx.global::<Settings>();
5280 let mode = self
5281 .soft_wrap_mode_override
5282 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5283 match mode {
5284 settings::SoftWrap::None => SoftWrap::None,
5285 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5286 settings::SoftWrap::PreferredLineLength => {
5287 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5288 }
5289 }
5290 }
5291
5292 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5293 self.soft_wrap_mode_override = Some(mode);
5294 cx.notify();
5295 }
5296
5297 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5298 self.display_map
5299 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5300 }
5301
5302 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5303 self.highlighted_rows = rows;
5304 }
5305
5306 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5307 self.highlighted_rows.clone()
5308 }
5309
5310 pub fn highlight_background<T: 'static>(
5311 &mut self,
5312 ranges: Vec<Range<Anchor>>,
5313 color_fetcher: fn(&Theme) -> Color,
5314 cx: &mut ViewContext<Self>,
5315 ) {
5316 self.background_highlights
5317 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5318 cx.notify();
5319 }
5320
5321 pub fn clear_background_highlights<T: 'static>(
5322 &mut self,
5323 cx: &mut ViewContext<Self>,
5324 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5325 cx.notify();
5326 self.background_highlights.remove(&TypeId::of::<T>())
5327 }
5328
5329 #[cfg(feature = "test-support")]
5330 pub fn all_background_highlights(
5331 &mut self,
5332 cx: &mut ViewContext<Self>,
5333 ) -> Vec<(Range<DisplayPoint>, Color)> {
5334 let snapshot = self.snapshot(cx);
5335 let buffer = &snapshot.buffer_snapshot;
5336 let start = buffer.anchor_before(0);
5337 let end = buffer.anchor_after(buffer.len());
5338 let theme = cx.global::<Settings>().theme.as_ref();
5339 self.background_highlights_in_range(start..end, &snapshot, theme)
5340 }
5341
5342 fn document_highlights_for_position<'a>(
5343 &'a self,
5344 position: Anchor,
5345 buffer: &'a MultiBufferSnapshot,
5346 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5347 let read_highlights = self
5348 .background_highlights
5349 .get(&TypeId::of::<DocumentHighlightRead>())
5350 .map(|h| &h.1);
5351 let write_highlights = self
5352 .background_highlights
5353 .get(&TypeId::of::<DocumentHighlightRead>())
5354 .map(|h| &h.1);
5355 let left_position = position.bias_left(buffer);
5356 let right_position = position.bias_right(buffer);
5357 read_highlights
5358 .into_iter()
5359 .chain(write_highlights)
5360 .flat_map(move |ranges| {
5361 let start_ix = match ranges.binary_search_by(|probe| {
5362 let cmp = probe.end.cmp(&left_position, &buffer);
5363 if cmp.is_ge() {
5364 Ordering::Greater
5365 } else {
5366 Ordering::Less
5367 }
5368 }) {
5369 Ok(i) | Err(i) => i,
5370 };
5371
5372 let right_position = right_position.clone();
5373 ranges[start_ix..]
5374 .iter()
5375 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5376 })
5377 }
5378
5379 pub fn background_highlights_in_range(
5380 &self,
5381 search_range: Range<Anchor>,
5382 display_snapshot: &DisplaySnapshot,
5383 theme: &Theme,
5384 ) -> Vec<(Range<DisplayPoint>, Color)> {
5385 let mut results = Vec::new();
5386 let buffer = &display_snapshot.buffer_snapshot;
5387 for (color_fetcher, ranges) in self.background_highlights.values() {
5388 let color = color_fetcher(theme);
5389 let start_ix = match ranges.binary_search_by(|probe| {
5390 let cmp = probe.end.cmp(&search_range.start, &buffer);
5391 if cmp.is_gt() {
5392 Ordering::Greater
5393 } else {
5394 Ordering::Less
5395 }
5396 }) {
5397 Ok(i) | Err(i) => i,
5398 };
5399 for range in &ranges[start_ix..] {
5400 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5401 break;
5402 }
5403 let start = range
5404 .start
5405 .to_point(buffer)
5406 .to_display_point(display_snapshot);
5407 let end = range
5408 .end
5409 .to_point(buffer)
5410 .to_display_point(display_snapshot);
5411 results.push((start..end, color))
5412 }
5413 }
5414 results
5415 }
5416
5417 pub fn highlight_text<T: 'static>(
5418 &mut self,
5419 ranges: Vec<Range<Anchor>>,
5420 style: HighlightStyle,
5421 cx: &mut ViewContext<Self>,
5422 ) {
5423 self.display_map.update(cx, |map, _| {
5424 map.highlight_text(TypeId::of::<T>(), ranges, style)
5425 });
5426 cx.notify();
5427 }
5428
5429 pub fn clear_text_highlights<T: 'static>(
5430 &mut self,
5431 cx: &mut ViewContext<Self>,
5432 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5433 cx.notify();
5434 self.display_map
5435 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5436 }
5437
5438 fn next_blink_epoch(&mut self) -> usize {
5439 self.blink_epoch += 1;
5440 self.blink_epoch
5441 }
5442
5443 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5444 if !self.focused {
5445 return;
5446 }
5447
5448 self.show_local_cursors = true;
5449 cx.notify();
5450
5451 let epoch = self.next_blink_epoch();
5452 cx.spawn(|this, mut cx| {
5453 let this = this.downgrade();
5454 async move {
5455 Timer::after(CURSOR_BLINK_INTERVAL).await;
5456 if let Some(this) = this.upgrade(&cx) {
5457 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5458 }
5459 }
5460 })
5461 .detach();
5462 }
5463
5464 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5465 if epoch == self.blink_epoch {
5466 self.blinking_paused = false;
5467 self.blink_cursors(epoch, cx);
5468 }
5469 }
5470
5471 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5472 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5473 self.show_local_cursors = !self.show_local_cursors;
5474 cx.notify();
5475
5476 let epoch = self.next_blink_epoch();
5477 cx.spawn(|this, mut cx| {
5478 let this = this.downgrade();
5479 async move {
5480 Timer::after(CURSOR_BLINK_INTERVAL).await;
5481 if let Some(this) = this.upgrade(&cx) {
5482 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5483 }
5484 }
5485 })
5486 .detach();
5487 }
5488 }
5489
5490 pub fn show_local_cursors(&self) -> bool {
5491 self.show_local_cursors && self.focused
5492 }
5493
5494 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5495 cx.notify();
5496 }
5497
5498 fn on_buffer_event(
5499 &mut self,
5500 _: ModelHandle<MultiBuffer>,
5501 event: &language::Event,
5502 cx: &mut ViewContext<Self>,
5503 ) {
5504 match event {
5505 language::Event::Edited => {
5506 self.refresh_active_diagnostics(cx);
5507 self.refresh_code_actions(cx);
5508 cx.emit(Event::BufferEdited);
5509 }
5510 language::Event::Reparsed => cx.emit(Event::Reparsed),
5511 language::Event::Dirtied => cx.emit(Event::Dirtied),
5512 language::Event::Saved => cx.emit(Event::Saved),
5513 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5514 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5515 language::Event::Closed => cx.emit(Event::Closed),
5516 language::Event::DiagnosticsUpdated => {
5517 self.refresh_active_diagnostics(cx);
5518 }
5519 _ => {}
5520 }
5521 }
5522
5523 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5524 cx.notify();
5525 }
5526
5527 pub fn set_searchable(&mut self, searchable: bool) {
5528 self.searchable = searchable;
5529 }
5530
5531 pub fn searchable(&self) -> bool {
5532 self.searchable
5533 }
5534
5535 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5536 let active_item = workspace.active_item(cx);
5537 let editor_handle = if let Some(editor) = active_item
5538 .as_ref()
5539 .and_then(|item| item.act_as::<Self>(cx))
5540 {
5541 editor
5542 } else {
5543 cx.propagate_action();
5544 return;
5545 };
5546
5547 let editor = editor_handle.read(cx);
5548 let buffer = editor.buffer.read(cx);
5549 if buffer.is_singleton() {
5550 cx.propagate_action();
5551 return;
5552 }
5553
5554 let mut new_selections_by_buffer = HashMap::default();
5555 for selection in editor.selections.all::<usize>(cx) {
5556 for (buffer, mut range) in
5557 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5558 {
5559 if selection.reversed {
5560 mem::swap(&mut range.start, &mut range.end);
5561 }
5562 new_selections_by_buffer
5563 .entry(buffer)
5564 .or_insert(Vec::new())
5565 .push(range)
5566 }
5567 }
5568
5569 editor_handle.update(cx, |editor, cx| {
5570 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5571 });
5572 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5573 nav_history.borrow_mut().disable();
5574
5575 // We defer the pane interaction because we ourselves are a workspace item
5576 // and activating a new item causes the pane to call a method on us reentrantly,
5577 // which panics if we're on the stack.
5578 cx.defer(move |workspace, cx| {
5579 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5580 let editor = workspace.open_project_item::<Self>(buffer, cx);
5581 editor.update(cx, |editor, cx| {
5582 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5583 s.select_ranges(ranges);
5584 });
5585 });
5586 }
5587
5588 nav_history.borrow_mut().enable();
5589 });
5590 }
5591
5592 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5593 let editor = workspace.open_path(action.path.clone(), true, cx);
5594 let position = action.position;
5595 let anchor = action.anchor;
5596 cx.spawn_weak(|_, mut cx| async move {
5597 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5598 editor.update(&mut cx, |editor, cx| {
5599 let buffer = editor.buffer().read(cx).as_singleton()?;
5600 let buffer = buffer.read(cx);
5601 let cursor = if buffer.can_resolve(&anchor) {
5602 language::ToPoint::to_point(&anchor, buffer)
5603 } else {
5604 buffer.clip_point(position, Bias::Left)
5605 };
5606
5607 let nav_history = editor.nav_history.take();
5608 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5609 s.select_ranges([cursor..cursor]);
5610 });
5611 editor.nav_history = nav_history;
5612
5613 Some(())
5614 })?;
5615 Some(())
5616 })
5617 .detach()
5618 }
5619}
5620
5621impl EditorSnapshot {
5622 pub fn is_focused(&self) -> bool {
5623 self.is_focused
5624 }
5625
5626 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5627 self.placeholder_text.as_ref()
5628 }
5629
5630 pub fn scroll_position(&self) -> Vector2F {
5631 compute_scroll_position(
5632 &self.display_snapshot,
5633 self.scroll_position,
5634 &self.scroll_top_anchor,
5635 )
5636 }
5637}
5638
5639impl Deref for EditorSnapshot {
5640 type Target = DisplaySnapshot;
5641
5642 fn deref(&self) -> &Self::Target {
5643 &self.display_snapshot
5644 }
5645}
5646
5647fn compute_scroll_position(
5648 snapshot: &DisplaySnapshot,
5649 mut scroll_position: Vector2F,
5650 scroll_top_anchor: &Anchor,
5651) -> Vector2F {
5652 if *scroll_top_anchor != Anchor::min() {
5653 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5654 scroll_position.set_y(scroll_top + scroll_position.y());
5655 } else {
5656 scroll_position.set_y(0.);
5657 }
5658 scroll_position
5659}
5660
5661#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5662pub enum Event {
5663 Activate,
5664 BufferEdited,
5665 Edited,
5666 Reparsed,
5667 Blurred,
5668 Dirtied,
5669 Saved,
5670 TitleChanged,
5671 SelectionsChanged { local: bool },
5672 ScrollPositionChanged { local: bool },
5673 Closed,
5674}
5675
5676pub struct EditorFocused(pub ViewHandle<Editor>);
5677pub struct EditorBlurred(pub ViewHandle<Editor>);
5678pub struct EditorReleased(pub WeakViewHandle<Editor>);
5679
5680impl Entity for Editor {
5681 type Event = Event;
5682
5683 fn release(&mut self, cx: &mut MutableAppContext) {
5684 cx.emit_global(EditorReleased(self.handle.clone()));
5685 }
5686}
5687
5688impl View for Editor {
5689 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5690 let style = self.style(cx);
5691 let font_changed = self.display_map.update(cx, |map, cx| {
5692 map.set_font(style.text.font_id, style.text.font_size, cx)
5693 });
5694
5695 // If the
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 cx.emit(Event::Blurred);
5743 cx.notify();
5744 }
5745
5746 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5747 let mut context = Self::default_keymap_context();
5748 let mode = match self.mode {
5749 EditorMode::SingleLine => "single_line",
5750 EditorMode::AutoHeight { .. } => "auto_height",
5751 EditorMode::Full => "full",
5752 };
5753 context.map.insert("mode".into(), mode.into());
5754 if self.pending_rename.is_some() {
5755 context.set.insert("renaming".into());
5756 }
5757 match self.context_menu.as_ref() {
5758 Some(ContextMenu::Completions(_)) => {
5759 context.set.insert("showing_completions".into());
5760 }
5761 Some(ContextMenu::CodeActions(_)) => {
5762 context.set.insert("showing_code_actions".into());
5763 }
5764 None => {}
5765 }
5766
5767 for layer in self.keymap_context_layers.values() {
5768 context.extend(layer);
5769 }
5770
5771 context
5772 }
5773}
5774
5775fn build_style(
5776 settings: &Settings,
5777 get_field_editor_theme: Option<GetFieldEditorTheme>,
5778 override_text_style: Option<&OverrideTextStyle>,
5779 cx: &AppContext,
5780) -> EditorStyle {
5781 let font_cache = cx.font_cache();
5782
5783 let mut theme = settings.theme.editor.clone();
5784 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5785 let field_editor_theme = get_field_editor_theme(&settings.theme);
5786 theme.text_color = field_editor_theme.text.color;
5787 theme.selection = field_editor_theme.selection;
5788 theme.background = field_editor_theme
5789 .container
5790 .background_color
5791 .unwrap_or_default();
5792 EditorStyle {
5793 text: field_editor_theme.text,
5794 placeholder_text: field_editor_theme.placeholder_text,
5795 theme,
5796 }
5797 } else {
5798 let font_family_id = settings.buffer_font_family;
5799 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5800 let font_properties = Default::default();
5801 let font_id = font_cache
5802 .select_font(font_family_id, &font_properties)
5803 .unwrap();
5804 let font_size = settings.buffer_font_size;
5805 EditorStyle {
5806 text: TextStyle {
5807 color: settings.theme.editor.text_color,
5808 font_family_name,
5809 font_family_id,
5810 font_id,
5811 font_size,
5812 font_properties,
5813 underline: Default::default(),
5814 },
5815 placeholder_text: None,
5816 theme,
5817 }
5818 };
5819
5820 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5821 if let Some(highlighted) = style
5822 .text
5823 .clone()
5824 .highlight(highlight_style, font_cache)
5825 .log_err()
5826 {
5827 style.text = highlighted;
5828 }
5829 }
5830
5831 style
5832}
5833
5834trait SelectionExt {
5835 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5836 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5837 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5838 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5839 -> Range<u32>;
5840}
5841
5842impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5843 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5844 let start = self.start.to_point(buffer);
5845 let end = self.end.to_point(buffer);
5846 if self.reversed {
5847 end..start
5848 } else {
5849 start..end
5850 }
5851 }
5852
5853 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5854 let start = self.start.to_offset(buffer);
5855 let end = self.end.to_offset(buffer);
5856 if self.reversed {
5857 end..start
5858 } else {
5859 start..end
5860 }
5861 }
5862
5863 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5864 let start = self
5865 .start
5866 .to_point(&map.buffer_snapshot)
5867 .to_display_point(map);
5868 let end = self
5869 .end
5870 .to_point(&map.buffer_snapshot)
5871 .to_display_point(map);
5872 if self.reversed {
5873 end..start
5874 } else {
5875 start..end
5876 }
5877 }
5878
5879 fn spanned_rows(
5880 &self,
5881 include_end_if_at_line_start: bool,
5882 map: &DisplaySnapshot,
5883 ) -> Range<u32> {
5884 let start = self.start.to_point(&map.buffer_snapshot);
5885 let mut end = self.end.to_point(&map.buffer_snapshot);
5886 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5887 end.row -= 1;
5888 }
5889
5890 let buffer_start = map.prev_line_boundary(start).0;
5891 let buffer_end = map.next_line_boundary(end).0;
5892 buffer_start.row..buffer_end.row + 1
5893 }
5894}
5895
5896impl<T: InvalidationRegion> InvalidationStack<T> {
5897 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5898 where
5899 S: Clone + ToOffset,
5900 {
5901 while let Some(region) = self.last() {
5902 let all_selections_inside_invalidation_ranges =
5903 if selections.len() == region.ranges().len() {
5904 selections
5905 .iter()
5906 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5907 .all(|(selection, invalidation_range)| {
5908 let head = selection.head().to_offset(&buffer);
5909 invalidation_range.start <= head && invalidation_range.end >= head
5910 })
5911 } else {
5912 false
5913 };
5914
5915 if all_selections_inside_invalidation_ranges {
5916 break;
5917 } else {
5918 self.pop();
5919 }
5920 }
5921 }
5922}
5923
5924impl<T> Default for InvalidationStack<T> {
5925 fn default() -> Self {
5926 Self(Default::default())
5927 }
5928}
5929
5930impl<T> Deref for InvalidationStack<T> {
5931 type Target = Vec<T>;
5932
5933 fn deref(&self) -> &Self::Target {
5934 &self.0
5935 }
5936}
5937
5938impl<T> DerefMut for InvalidationStack<T> {
5939 fn deref_mut(&mut self) -> &mut Self::Target {
5940 &mut self.0
5941 }
5942}
5943
5944impl InvalidationRegion for BracketPairState {
5945 fn ranges(&self) -> &[Range<Anchor>] {
5946 &self.ranges
5947 }
5948}
5949
5950impl InvalidationRegion for SnippetState {
5951 fn ranges(&self) -> &[Range<Anchor>] {
5952 &self.ranges[self.active_index]
5953 }
5954}
5955
5956impl Deref for EditorStyle {
5957 type Target = theme::Editor;
5958
5959 fn deref(&self) -> &Self::Target {
5960 &self.theme
5961 }
5962}
5963
5964pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
5965 let mut highlighted_lines = Vec::new();
5966 for line in diagnostic.message.lines() {
5967 highlighted_lines.push(highlight_diagnostic_message(line));
5968 }
5969
5970 Arc::new(move |cx: &mut BlockContext| {
5971 let settings = cx.global::<Settings>();
5972 let theme = &settings.theme.editor;
5973 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
5974 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
5975 Flex::column()
5976 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
5977 Label::new(
5978 line.clone(),
5979 style.message.clone().with_font_size(font_size),
5980 )
5981 .with_highlights(highlights.clone())
5982 .contained()
5983 .with_margin_left(cx.anchor_x)
5984 .boxed()
5985 }))
5986 .aligned()
5987 .left()
5988 .boxed()
5989 })
5990}
5991
5992pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
5993 let mut message_without_backticks = String::new();
5994 let mut prev_offset = 0;
5995 let mut inside_block = false;
5996 let mut highlights = Vec::new();
5997 for (match_ix, (offset, _)) in message
5998 .match_indices('`')
5999 .chain([(message.len(), "")])
6000 .enumerate()
6001 {
6002 message_without_backticks.push_str(&message[prev_offset..offset]);
6003 if inside_block {
6004 highlights.extend(prev_offset - match_ix..offset - match_ix);
6005 }
6006
6007 inside_block = !inside_block;
6008 prev_offset = offset + 1;
6009 }
6010
6011 (message_without_backticks, highlights)
6012}
6013
6014pub fn diagnostic_style(
6015 severity: DiagnosticSeverity,
6016 valid: bool,
6017 theme: &theme::Editor,
6018) -> DiagnosticStyle {
6019 match (severity, valid) {
6020 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6021 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6022 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6023 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6024 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6025 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6026 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6027 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6028 _ => theme.invalid_hint_diagnostic.clone(),
6029 }
6030}
6031
6032pub fn combine_syntax_and_fuzzy_match_highlights(
6033 text: &str,
6034 default_style: HighlightStyle,
6035 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6036 match_indices: &[usize],
6037) -> Vec<(Range<usize>, HighlightStyle)> {
6038 let mut result = Vec::new();
6039 let mut match_indices = match_indices.iter().copied().peekable();
6040
6041 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6042 {
6043 syntax_highlight.weight = None;
6044
6045 // Add highlights for any fuzzy match characters before the next
6046 // syntax highlight range.
6047 while let Some(&match_index) = match_indices.peek() {
6048 if match_index >= range.start {
6049 break;
6050 }
6051 match_indices.next();
6052 let end_index = char_ix_after(match_index, text);
6053 let mut match_style = default_style;
6054 match_style.weight = Some(fonts::Weight::BOLD);
6055 result.push((match_index..end_index, match_style));
6056 }
6057
6058 if range.start == usize::MAX {
6059 break;
6060 }
6061
6062 // Add highlights for any fuzzy match characters within the
6063 // syntax highlight range.
6064 let mut offset = range.start;
6065 while let Some(&match_index) = match_indices.peek() {
6066 if match_index >= range.end {
6067 break;
6068 }
6069
6070 match_indices.next();
6071 if match_index > offset {
6072 result.push((offset..match_index, syntax_highlight));
6073 }
6074
6075 let mut end_index = char_ix_after(match_index, text);
6076 while let Some(&next_match_index) = match_indices.peek() {
6077 if next_match_index == end_index && next_match_index < range.end {
6078 end_index = char_ix_after(next_match_index, text);
6079 match_indices.next();
6080 } else {
6081 break;
6082 }
6083 }
6084
6085 let mut match_style = syntax_highlight;
6086 match_style.weight = Some(fonts::Weight::BOLD);
6087 result.push((match_index..end_index, match_style));
6088 offset = end_index;
6089 }
6090
6091 if offset < range.end {
6092 result.push((offset..range.end, syntax_highlight));
6093 }
6094 }
6095
6096 fn char_ix_after(ix: usize, text: &str) -> usize {
6097 ix + text[ix..].chars().next().unwrap().len_utf8()
6098 }
6099
6100 result
6101}
6102
6103pub fn styled_runs_for_code_label<'a>(
6104 label: &'a CodeLabel,
6105 syntax_theme: &'a theme::SyntaxTheme,
6106) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6107 let fade_out = HighlightStyle {
6108 fade_out: Some(0.35),
6109 ..Default::default()
6110 };
6111
6112 let mut prev_end = label.filter_range.end;
6113 label
6114 .runs
6115 .iter()
6116 .enumerate()
6117 .flat_map(move |(ix, (range, highlight_id))| {
6118 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6119 style
6120 } else {
6121 return Default::default();
6122 };
6123 let mut muted_style = style.clone();
6124 muted_style.highlight(fade_out);
6125
6126 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6127 if range.start >= label.filter_range.end {
6128 if range.start > prev_end {
6129 runs.push((prev_end..range.start, fade_out));
6130 }
6131 runs.push((range.clone(), muted_style));
6132 } else if range.end <= label.filter_range.end {
6133 runs.push((range.clone(), style));
6134 } else {
6135 runs.push((range.start..label.filter_range.end, style));
6136 runs.push((label.filter_range.end..range.end, muted_style));
6137 }
6138 prev_end = cmp::max(prev_end, range.end);
6139
6140 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6141 runs.push((prev_end..label.text.len(), fade_out));
6142 }
6143
6144 runs
6145 })
6146}
6147
6148#[cfg(test)]
6149mod tests {
6150 use crate::{
6151 hover_popover::{hover, hover_at, HoverAt, HOVER_DELAY_MILLIS, HOVER_GRACE_MILLIS},
6152 test::{
6153 assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext,
6154 EditorTestContext,
6155 },
6156 };
6157
6158 use super::*;
6159 use futures::StreamExt;
6160 use gpui::{
6161 geometry::rect::RectF,
6162 platform::{WindowBounds, WindowOptions},
6163 };
6164 use indoc::indoc;
6165 use language::{FakeLspAdapter, LanguageConfig};
6166 use lsp::FakeLanguageServer;
6167 use project::{FakeFs, HoverBlock};
6168 use settings::LanguageOverride;
6169 use std::{cell::RefCell, rc::Rc, time::Instant};
6170 use text::Point;
6171 use unindent::Unindent;
6172 use util::{
6173 assert_set_eq,
6174 test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
6175 };
6176 use workspace::{FollowableItem, ItemHandle};
6177
6178 #[gpui::test]
6179 fn test_edit_events(cx: &mut MutableAppContext) {
6180 cx.set_global(Settings::test(cx));
6181 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6182
6183 let events = Rc::new(RefCell::new(Vec::new()));
6184 let (_, editor1) = cx.add_window(Default::default(), {
6185 let events = events.clone();
6186 |cx| {
6187 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6188 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
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!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6201 events.borrow_mut().push(("editor2", *event));
6202 }
6203 })
6204 .detach();
6205 Editor::for_buffer(buffer.clone(), None, cx)
6206 }
6207 });
6208 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6209
6210 // Mutating editor 1 will emit an `Edited` event only for that editor.
6211 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6212 assert_eq!(
6213 mem::take(&mut *events.borrow_mut()),
6214 [
6215 ("editor1", Event::Edited),
6216 ("editor1", Event::BufferEdited),
6217 ("editor2", Event::BufferEdited),
6218 ("editor1", Event::Dirtied),
6219 ("editor2", Event::Dirtied)
6220 ]
6221 );
6222
6223 // Mutating editor 2 will emit an `Edited` event only for that editor.
6224 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6225 assert_eq!(
6226 mem::take(&mut *events.borrow_mut()),
6227 [
6228 ("editor2", Event::Edited),
6229 ("editor1", Event::BufferEdited),
6230 ("editor2", Event::BufferEdited),
6231 ]
6232 );
6233
6234 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6235 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6236 assert_eq!(
6237 mem::take(&mut *events.borrow_mut()),
6238 [
6239 ("editor1", Event::Edited),
6240 ("editor1", Event::BufferEdited),
6241 ("editor2", Event::BufferEdited),
6242 ]
6243 );
6244
6245 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6246 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6247 assert_eq!(
6248 mem::take(&mut *events.borrow_mut()),
6249 [
6250 ("editor1", Event::Edited),
6251 ("editor1", Event::BufferEdited),
6252 ("editor2", Event::BufferEdited),
6253 ]
6254 );
6255
6256 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6257 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6258 assert_eq!(
6259 mem::take(&mut *events.borrow_mut()),
6260 [
6261 ("editor2", Event::Edited),
6262 ("editor1", Event::BufferEdited),
6263 ("editor2", Event::BufferEdited),
6264 ]
6265 );
6266
6267 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6268 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6269 assert_eq!(
6270 mem::take(&mut *events.borrow_mut()),
6271 [
6272 ("editor2", Event::Edited),
6273 ("editor1", Event::BufferEdited),
6274 ("editor2", Event::BufferEdited),
6275 ]
6276 );
6277
6278 // No event is emitted when the mutation is a no-op.
6279 editor2.update(cx, |editor, cx| {
6280 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6281
6282 editor.backspace(&Backspace, cx);
6283 });
6284 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6285 }
6286
6287 #[gpui::test]
6288 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6289 cx.set_global(Settings::test(cx));
6290 let mut now = Instant::now();
6291 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6292 let group_interval = buffer.read(cx).transaction_group_interval();
6293 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6294 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6295
6296 editor.update(cx, |editor, cx| {
6297 editor.start_transaction_at(now, cx);
6298 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6299
6300 editor.insert("cd", cx);
6301 editor.end_transaction_at(now, cx);
6302 assert_eq!(editor.text(cx), "12cd56");
6303 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6304
6305 editor.start_transaction_at(now, cx);
6306 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6307 editor.insert("e", cx);
6308 editor.end_transaction_at(now, cx);
6309 assert_eq!(editor.text(cx), "12cde6");
6310 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6311
6312 now += group_interval + Duration::from_millis(1);
6313 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6314
6315 // Simulate an edit in another editor
6316 buffer.update(cx, |buffer, cx| {
6317 buffer.start_transaction_at(now, cx);
6318 buffer.edit([(0..1, "a")], cx);
6319 buffer.edit([(1..1, "b")], cx);
6320 buffer.end_transaction_at(now, cx);
6321 });
6322
6323 assert_eq!(editor.text(cx), "ab2cde6");
6324 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6325
6326 // Last transaction happened past the group interval in a different editor.
6327 // Undo it individually and don't restore selections.
6328 editor.undo(&Undo, cx);
6329 assert_eq!(editor.text(cx), "12cde6");
6330 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6331
6332 // First two transactions happened within the group interval in this editor.
6333 // Undo them together and restore selections.
6334 editor.undo(&Undo, cx);
6335 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6336 assert_eq!(editor.text(cx), "123456");
6337 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6338
6339 // Redo the first two transactions together.
6340 editor.redo(&Redo, cx);
6341 assert_eq!(editor.text(cx), "12cde6");
6342 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6343
6344 // Redo the last transaction on its own.
6345 editor.redo(&Redo, cx);
6346 assert_eq!(editor.text(cx), "ab2cde6");
6347 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6348
6349 // Test empty transactions.
6350 editor.start_transaction_at(now, cx);
6351 editor.end_transaction_at(now, cx);
6352 editor.undo(&Undo, cx);
6353 assert_eq!(editor.text(cx), "12cde6");
6354 });
6355 }
6356
6357 #[gpui::test]
6358 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6359 cx.set_global(Settings::test(cx));
6360
6361 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6362 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6363 editor.update(cx, |view, cx| {
6364 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6365 });
6366 assert_eq!(
6367 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6368 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6369 );
6370
6371 editor.update(cx, |view, cx| {
6372 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6373 });
6374
6375 assert_eq!(
6376 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6377 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6378 );
6379
6380 editor.update(cx, |view, cx| {
6381 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6382 });
6383
6384 assert_eq!(
6385 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6386 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6387 );
6388
6389 editor.update(cx, |view, cx| {
6390 view.end_selection(cx);
6391 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6392 });
6393
6394 assert_eq!(
6395 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6396 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6397 );
6398
6399 editor.update(cx, |view, cx| {
6400 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6401 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6402 });
6403
6404 assert_eq!(
6405 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6406 [
6407 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6408 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6409 ]
6410 );
6411
6412 editor.update(cx, |view, cx| {
6413 view.end_selection(cx);
6414 });
6415
6416 assert_eq!(
6417 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6418 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6419 );
6420 }
6421
6422 #[gpui::test]
6423 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6424 cx.set_global(Settings::test(cx));
6425 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6426 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6427
6428 view.update(cx, |view, cx| {
6429 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6430 assert_eq!(
6431 view.selections.display_ranges(cx),
6432 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6433 );
6434 });
6435
6436 view.update(cx, |view, cx| {
6437 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6438 assert_eq!(
6439 view.selections.display_ranges(cx),
6440 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6441 );
6442 });
6443
6444 view.update(cx, |view, cx| {
6445 view.cancel(&Cancel, cx);
6446 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6447 assert_eq!(
6448 view.selections.display_ranges(cx),
6449 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6450 );
6451 });
6452 }
6453
6454 #[gpui::test]
6455 fn test_clone_with_selections(cx: &mut gpui::MutableAppContext) {
6456 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6457 The qu[ick brown
6458 fox jum]ps over
6459 the lazy dog
6460 "});
6461 cx.set_global(Settings::test(cx));
6462 let buffer = MultiBuffer::build_simple(&text, cx);
6463
6464 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6465
6466 let cloned_editor = view.update(cx, |view, cx| {
6467 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6468 view.clone(cx)
6469 });
6470
6471 assert_set_eq!(cloned_editor.selections.ranges(cx), selection_ranges);
6472 }
6473
6474 #[gpui::test]
6475 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6476 cx.set_global(Settings::test(cx));
6477 use workspace::Item;
6478 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6479 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6480
6481 cx.add_window(Default::default(), |cx| {
6482 let mut editor = build_editor(buffer.clone(), cx);
6483 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6484
6485 // Move the cursor a small distance.
6486 // Nothing is added to the navigation history.
6487 editor.change_selections(None, cx, |s| {
6488 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6489 });
6490 editor.change_selections(None, cx, |s| {
6491 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6492 });
6493 assert!(nav_history.borrow_mut().pop_backward().is_none());
6494
6495 // Move the cursor a large distance.
6496 // The history can jump back to the previous position.
6497 editor.change_selections(None, cx, |s| {
6498 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6499 });
6500 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6501 editor.navigate(nav_entry.data.unwrap(), cx);
6502 assert_eq!(nav_entry.item.id(), cx.view_id());
6503 assert_eq!(
6504 editor.selections.display_ranges(cx),
6505 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6506 );
6507 assert!(nav_history.borrow_mut().pop_backward().is_none());
6508
6509 // Move the cursor a small distance via the mouse.
6510 // Nothing is added to the navigation history.
6511 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6512 editor.end_selection(cx);
6513 assert_eq!(
6514 editor.selections.display_ranges(cx),
6515 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6516 );
6517 assert!(nav_history.borrow_mut().pop_backward().is_none());
6518
6519 // Move the cursor a large distance via the mouse.
6520 // The history can jump back to the previous position.
6521 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6522 editor.end_selection(cx);
6523 assert_eq!(
6524 editor.selections.display_ranges(cx),
6525 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6526 );
6527 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6528 editor.navigate(nav_entry.data.unwrap(), cx);
6529 assert_eq!(nav_entry.item.id(), cx.view_id());
6530 assert_eq!(
6531 editor.selections.display_ranges(cx),
6532 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6533 );
6534 assert!(nav_history.borrow_mut().pop_backward().is_none());
6535
6536 // Set scroll position to check later
6537 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6538 let original_scroll_position = editor.scroll_position;
6539 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6540
6541 // Jump to the end of the document and adjust scroll
6542 editor.move_to_end(&MoveToEnd, cx);
6543 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6544 assert_ne!(editor.scroll_position, original_scroll_position);
6545 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6546
6547 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6548 editor.navigate(nav_entry.data.unwrap(), cx);
6549 assert_eq!(editor.scroll_position, original_scroll_position);
6550 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6551
6552 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6553 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6554 invalid_anchor.text_anchor.buffer_id = Some(999);
6555 let invalid_point = Point::new(9999, 0);
6556 editor.navigate(
6557 Box::new(NavigationData {
6558 cursor_anchor: invalid_anchor.clone(),
6559 cursor_position: invalid_point,
6560 scroll_top_anchor: invalid_anchor.clone(),
6561 scroll_top_row: invalid_point.row,
6562 scroll_position: Default::default(),
6563 }),
6564 cx,
6565 );
6566 assert_eq!(
6567 editor.selections.display_ranges(cx),
6568 &[editor.max_point(cx)..editor.max_point(cx)]
6569 );
6570 assert_eq!(
6571 editor.scroll_position(cx),
6572 vec2f(0., editor.max_point(cx).row() as f32)
6573 );
6574
6575 editor
6576 });
6577 }
6578
6579 #[gpui::test]
6580 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6581 cx.set_global(Settings::test(cx));
6582 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6583 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6584
6585 view.update(cx, |view, cx| {
6586 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6587 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6588 view.end_selection(cx);
6589
6590 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6591 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6592 view.end_selection(cx);
6593 assert_eq!(
6594 view.selections.display_ranges(cx),
6595 [
6596 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6597 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6598 ]
6599 );
6600 });
6601
6602 view.update(cx, |view, cx| {
6603 view.cancel(&Cancel, cx);
6604 assert_eq!(
6605 view.selections.display_ranges(cx),
6606 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6607 );
6608 });
6609
6610 view.update(cx, |view, cx| {
6611 view.cancel(&Cancel, cx);
6612 assert_eq!(
6613 view.selections.display_ranges(cx),
6614 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6615 );
6616 });
6617 }
6618
6619 #[gpui::test]
6620 fn test_fold(cx: &mut gpui::MutableAppContext) {
6621 cx.set_global(Settings::test(cx));
6622 let buffer = MultiBuffer::build_simple(
6623 &"
6624 impl Foo {
6625 // Hello!
6626
6627 fn a() {
6628 1
6629 }
6630
6631 fn b() {
6632 2
6633 }
6634
6635 fn c() {
6636 3
6637 }
6638 }
6639 "
6640 .unindent(),
6641 cx,
6642 );
6643 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6644
6645 view.update(cx, |view, cx| {
6646 view.change_selections(None, cx, |s| {
6647 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6648 });
6649 view.fold(&Fold, cx);
6650 assert_eq!(
6651 view.display_text(cx),
6652 "
6653 impl Foo {
6654 // Hello!
6655
6656 fn a() {
6657 1
6658 }
6659
6660 fn b() {…
6661 }
6662
6663 fn c() {…
6664 }
6665 }
6666 "
6667 .unindent(),
6668 );
6669
6670 view.fold(&Fold, cx);
6671 assert_eq!(
6672 view.display_text(cx),
6673 "
6674 impl Foo {…
6675 }
6676 "
6677 .unindent(),
6678 );
6679
6680 view.unfold_lines(&UnfoldLines, cx);
6681 assert_eq!(
6682 view.display_text(cx),
6683 "
6684 impl Foo {
6685 // Hello!
6686
6687 fn a() {
6688 1
6689 }
6690
6691 fn b() {…
6692 }
6693
6694 fn c() {…
6695 }
6696 }
6697 "
6698 .unindent(),
6699 );
6700
6701 view.unfold_lines(&UnfoldLines, cx);
6702 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6703 });
6704 }
6705
6706 #[gpui::test]
6707 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6708 cx.set_global(Settings::test(cx));
6709 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6710 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6711
6712 buffer.update(cx, |buffer, cx| {
6713 buffer.edit(
6714 vec![
6715 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6716 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6717 ],
6718 cx,
6719 );
6720 });
6721
6722 view.update(cx, |view, cx| {
6723 assert_eq!(
6724 view.selections.display_ranges(cx),
6725 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6726 );
6727
6728 view.move_down(&MoveDown, cx);
6729 assert_eq!(
6730 view.selections.display_ranges(cx),
6731 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6732 );
6733
6734 view.move_right(&MoveRight, cx);
6735 assert_eq!(
6736 view.selections.display_ranges(cx),
6737 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6738 );
6739
6740 view.move_left(&MoveLeft, cx);
6741 assert_eq!(
6742 view.selections.display_ranges(cx),
6743 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6744 );
6745
6746 view.move_up(&MoveUp, cx);
6747 assert_eq!(
6748 view.selections.display_ranges(cx),
6749 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6750 );
6751
6752 view.move_to_end(&MoveToEnd, cx);
6753 assert_eq!(
6754 view.selections.display_ranges(cx),
6755 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6756 );
6757
6758 view.move_to_beginning(&MoveToBeginning, cx);
6759 assert_eq!(
6760 view.selections.display_ranges(cx),
6761 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6762 );
6763
6764 view.change_selections(None, cx, |s| {
6765 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6766 });
6767 view.select_to_beginning(&SelectToBeginning, cx);
6768 assert_eq!(
6769 view.selections.display_ranges(cx),
6770 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6771 );
6772
6773 view.select_to_end(&SelectToEnd, cx);
6774 assert_eq!(
6775 view.selections.display_ranges(cx),
6776 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6777 );
6778 });
6779 }
6780
6781 #[gpui::test]
6782 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6783 cx.set_global(Settings::test(cx));
6784 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6785 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6786
6787 assert_eq!('ⓐ'.len_utf8(), 3);
6788 assert_eq!('α'.len_utf8(), 2);
6789
6790 view.update(cx, |view, cx| {
6791 view.fold_ranges(
6792 vec![
6793 Point::new(0, 6)..Point::new(0, 12),
6794 Point::new(1, 2)..Point::new(1, 4),
6795 Point::new(2, 4)..Point::new(2, 8),
6796 ],
6797 cx,
6798 );
6799 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6800
6801 view.move_right(&MoveRight, cx);
6802 assert_eq!(
6803 view.selections.display_ranges(cx),
6804 &[empty_range(0, "ⓐ".len())]
6805 );
6806 view.move_right(&MoveRight, cx);
6807 assert_eq!(
6808 view.selections.display_ranges(cx),
6809 &[empty_range(0, "ⓐⓑ".len())]
6810 );
6811 view.move_right(&MoveRight, cx);
6812 assert_eq!(
6813 view.selections.display_ranges(cx),
6814 &[empty_range(0, "ⓐⓑ…".len())]
6815 );
6816
6817 view.move_down(&MoveDown, cx);
6818 assert_eq!(
6819 view.selections.display_ranges(cx),
6820 &[empty_range(1, "ab…".len())]
6821 );
6822 view.move_left(&MoveLeft, cx);
6823 assert_eq!(
6824 view.selections.display_ranges(cx),
6825 &[empty_range(1, "ab".len())]
6826 );
6827 view.move_left(&MoveLeft, cx);
6828 assert_eq!(
6829 view.selections.display_ranges(cx),
6830 &[empty_range(1, "a".len())]
6831 );
6832
6833 view.move_down(&MoveDown, cx);
6834 assert_eq!(
6835 view.selections.display_ranges(cx),
6836 &[empty_range(2, "α".len())]
6837 );
6838 view.move_right(&MoveRight, cx);
6839 assert_eq!(
6840 view.selections.display_ranges(cx),
6841 &[empty_range(2, "αβ".len())]
6842 );
6843 view.move_right(&MoveRight, cx);
6844 assert_eq!(
6845 view.selections.display_ranges(cx),
6846 &[empty_range(2, "αβ…".len())]
6847 );
6848 view.move_right(&MoveRight, cx);
6849 assert_eq!(
6850 view.selections.display_ranges(cx),
6851 &[empty_range(2, "αβ…ε".len())]
6852 );
6853
6854 view.move_up(&MoveUp, cx);
6855 assert_eq!(
6856 view.selections.display_ranges(cx),
6857 &[empty_range(1, "ab…e".len())]
6858 );
6859 view.move_up(&MoveUp, cx);
6860 assert_eq!(
6861 view.selections.display_ranges(cx),
6862 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6863 );
6864 view.move_left(&MoveLeft, cx);
6865 assert_eq!(
6866 view.selections.display_ranges(cx),
6867 &[empty_range(0, "ⓐⓑ…".len())]
6868 );
6869 view.move_left(&MoveLeft, cx);
6870 assert_eq!(
6871 view.selections.display_ranges(cx),
6872 &[empty_range(0, "ⓐⓑ".len())]
6873 );
6874 view.move_left(&MoveLeft, cx);
6875 assert_eq!(
6876 view.selections.display_ranges(cx),
6877 &[empty_range(0, "ⓐ".len())]
6878 );
6879 });
6880 }
6881
6882 #[gpui::test]
6883 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6884 cx.set_global(Settings::test(cx));
6885 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
6886 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6887 view.update(cx, |view, cx| {
6888 view.change_selections(None, cx, |s| {
6889 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
6890 });
6891 view.move_down(&MoveDown, cx);
6892 assert_eq!(
6893 view.selections.display_ranges(cx),
6894 &[empty_range(1, "abcd".len())]
6895 );
6896
6897 view.move_down(&MoveDown, cx);
6898 assert_eq!(
6899 view.selections.display_ranges(cx),
6900 &[empty_range(2, "αβγ".len())]
6901 );
6902
6903 view.move_down(&MoveDown, cx);
6904 assert_eq!(
6905 view.selections.display_ranges(cx),
6906 &[empty_range(3, "abcd".len())]
6907 );
6908
6909 view.move_down(&MoveDown, cx);
6910 assert_eq!(
6911 view.selections.display_ranges(cx),
6912 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
6913 );
6914
6915 view.move_up(&MoveUp, cx);
6916 assert_eq!(
6917 view.selections.display_ranges(cx),
6918 &[empty_range(3, "abcd".len())]
6919 );
6920
6921 view.move_up(&MoveUp, cx);
6922 assert_eq!(
6923 view.selections.display_ranges(cx),
6924 &[empty_range(2, "αβγ".len())]
6925 );
6926 });
6927 }
6928
6929 #[gpui::test]
6930 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
6931 cx.set_global(Settings::test(cx));
6932 let buffer = MultiBuffer::build_simple("abc\n def", cx);
6933 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6934 view.update(cx, |view, cx| {
6935 view.change_selections(None, cx, |s| {
6936 s.select_display_ranges([
6937 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6938 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6939 ]);
6940 });
6941 });
6942
6943 view.update(cx, |view, cx| {
6944 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6945 assert_eq!(
6946 view.selections.display_ranges(cx),
6947 &[
6948 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6949 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
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, 0)..DisplayPoint::new(1, 0),
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, 2)..DisplayPoint::new(1, 2),
6972 ]
6973 );
6974 });
6975
6976 view.update(cx, |view, cx| {
6977 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6978 assert_eq!(
6979 view.selections.display_ranges(cx),
6980 &[
6981 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6982 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6983 ]
6984 );
6985 });
6986
6987 // Moving to the end of line again is a no-op.
6988 view.update(cx, |view, cx| {
6989 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6990 assert_eq!(
6991 view.selections.display_ranges(cx),
6992 &[
6993 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6994 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6995 ]
6996 );
6997 });
6998
6999 view.update(cx, |view, cx| {
7000 view.move_left(&MoveLeft, cx);
7001 view.select_to_beginning_of_line(
7002 &SelectToBeginningOfLine {
7003 stop_at_soft_wraps: true,
7004 },
7005 cx,
7006 );
7007 assert_eq!(
7008 view.selections.display_ranges(cx),
7009 &[
7010 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7011 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7012 ]
7013 );
7014 });
7015
7016 view.update(cx, |view, cx| {
7017 view.select_to_beginning_of_line(
7018 &SelectToBeginningOfLine {
7019 stop_at_soft_wraps: true,
7020 },
7021 cx,
7022 );
7023 assert_eq!(
7024 view.selections.display_ranges(cx),
7025 &[
7026 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7027 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7028 ]
7029 );
7030 });
7031
7032 view.update(cx, |view, cx| {
7033 view.select_to_beginning_of_line(
7034 &SelectToBeginningOfLine {
7035 stop_at_soft_wraps: true,
7036 },
7037 cx,
7038 );
7039 assert_eq!(
7040 view.selections.display_ranges(cx),
7041 &[
7042 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7043 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7044 ]
7045 );
7046 });
7047
7048 view.update(cx, |view, cx| {
7049 view.select_to_end_of_line(
7050 &SelectToEndOfLine {
7051 stop_at_soft_wraps: true,
7052 },
7053 cx,
7054 );
7055 assert_eq!(
7056 view.selections.display_ranges(cx),
7057 &[
7058 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7059 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7060 ]
7061 );
7062 });
7063
7064 view.update(cx, |view, cx| {
7065 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7066 assert_eq!(view.display_text(cx), "ab\n de");
7067 assert_eq!(
7068 view.selections.display_ranges(cx),
7069 &[
7070 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7071 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7072 ]
7073 );
7074 });
7075
7076 view.update(cx, |view, cx| {
7077 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7078 assert_eq!(view.display_text(cx), "\n");
7079 assert_eq!(
7080 view.selections.display_ranges(cx),
7081 &[
7082 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7083 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7084 ]
7085 );
7086 });
7087 }
7088
7089 #[gpui::test]
7090 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7091 cx.set_global(Settings::test(cx));
7092 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7093 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7094 view.update(cx, |view, cx| {
7095 view.change_selections(None, cx, |s| {
7096 s.select_display_ranges([
7097 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7098 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7099 ])
7100 });
7101
7102 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7103 assert_selection_ranges(
7104 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7105 vec![('<', '>'), ('[', ']')],
7106 view,
7107 cx,
7108 );
7109
7110 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7111 assert_selection_ranges(
7112 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7113 vec![('<', '>'), ('[', ']')],
7114 view,
7115 cx,
7116 );
7117
7118 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7119 assert_selection_ranges(
7120 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7121 vec![('<', '>'), ('[', ']')],
7122 view,
7123 cx,
7124 );
7125
7126 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7127 assert_selection_ranges(
7128 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7129 vec![('<', '>'), ('[', ']')],
7130 view,
7131 cx,
7132 );
7133
7134 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7135 assert_selection_ranges(
7136 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7137 vec![('<', '>'), ('[', ']')],
7138 view,
7139 cx,
7140 );
7141
7142 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7143 assert_selection_ranges(
7144 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7145 vec![('<', '>'), ('[', ']')],
7146 view,
7147 cx,
7148 );
7149
7150 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7151 assert_selection_ranges(
7152 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7153 vec![('<', '>'), ('[', ']')],
7154 view,
7155 cx,
7156 );
7157
7158 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7159 assert_selection_ranges(
7160 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7161 vec![('<', '>'), ('[', ']')],
7162 view,
7163 cx,
7164 );
7165
7166 view.move_right(&MoveRight, cx);
7167 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7168 assert_selection_ranges(
7169 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7170 vec![('<', '>'), ('[', ']')],
7171 view,
7172 cx,
7173 );
7174
7175 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7176 assert_selection_ranges(
7177 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7178 vec![('<', '>'), ('[', ']')],
7179 view,
7180 cx,
7181 );
7182
7183 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7184 assert_selection_ranges(
7185 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7186 vec![('<', '>'), ('[', ']')],
7187 view,
7188 cx,
7189 );
7190 });
7191 }
7192
7193 #[gpui::test]
7194 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7195 cx.set_global(Settings::test(cx));
7196 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7197 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7198
7199 view.update(cx, |view, cx| {
7200 view.set_wrap_width(Some(140.), cx);
7201 assert_eq!(
7202 view.display_text(cx),
7203 "use one::{\n two::three::\n four::five\n};"
7204 );
7205
7206 view.change_selections(None, cx, |s| {
7207 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7208 });
7209
7210 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7211 assert_eq!(
7212 view.selections.display_ranges(cx),
7213 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7214 );
7215
7216 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7217 assert_eq!(
7218 view.selections.display_ranges(cx),
7219 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7220 );
7221
7222 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7223 assert_eq!(
7224 view.selections.display_ranges(cx),
7225 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7226 );
7227
7228 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7229 assert_eq!(
7230 view.selections.display_ranges(cx),
7231 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7232 );
7233
7234 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7235 assert_eq!(
7236 view.selections.display_ranges(cx),
7237 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7238 );
7239
7240 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7241 assert_eq!(
7242 view.selections.display_ranges(cx),
7243 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7244 );
7245 });
7246 }
7247
7248 #[gpui::test]
7249 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7250 cx.set_global(Settings::test(cx));
7251 let (text, ranges) = marked_text_ranges("one [two three] four");
7252 let buffer = MultiBuffer::build_simple(&text, cx);
7253
7254 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7255
7256 editor.update(cx, |editor, cx| {
7257 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7258 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7259 assert_eq!(editor.text(cx), " four");
7260 });
7261 }
7262
7263 #[gpui::test]
7264 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7265 cx.set_global(Settings::test(cx));
7266 let buffer = MultiBuffer::build_simple("one two three four", cx);
7267 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7268
7269 view.update(cx, |view, cx| {
7270 view.change_selections(None, cx, |s| {
7271 s.select_display_ranges([
7272 // an empty selection - the preceding word fragment is deleted
7273 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7274 // characters selected - they are deleted
7275 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7276 ])
7277 });
7278 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7279 });
7280
7281 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7282
7283 view.update(cx, |view, cx| {
7284 view.change_selections(None, cx, |s| {
7285 s.select_display_ranges([
7286 // an empty selection - the following word fragment is deleted
7287 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7288 // characters selected - they are deleted
7289 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7290 ])
7291 });
7292 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7293 });
7294
7295 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7296 }
7297
7298 #[gpui::test]
7299 fn test_newline(cx: &mut gpui::MutableAppContext) {
7300 cx.set_global(Settings::test(cx));
7301 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7302 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7303
7304 view.update(cx, |view, cx| {
7305 view.change_selections(None, cx, |s| {
7306 s.select_display_ranges([
7307 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7308 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7309 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7310 ])
7311 });
7312
7313 view.newline(&Newline, cx);
7314 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7315 });
7316 }
7317
7318 #[gpui::test]
7319 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7320 cx.set_global(Settings::test(cx));
7321 let buffer = MultiBuffer::build_simple(
7322 "
7323 a
7324 b(
7325 X
7326 )
7327 c(
7328 X
7329 )
7330 "
7331 .unindent()
7332 .as_str(),
7333 cx,
7334 );
7335
7336 let (_, editor) = cx.add_window(Default::default(), |cx| {
7337 let mut editor = build_editor(buffer.clone(), cx);
7338 editor.change_selections(None, cx, |s| {
7339 s.select_ranges([
7340 Point::new(2, 4)..Point::new(2, 5),
7341 Point::new(5, 4)..Point::new(5, 5),
7342 ])
7343 });
7344 editor
7345 });
7346
7347 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7348 buffer.update(cx, |buffer, cx| {
7349 buffer.edit(
7350 [
7351 (Point::new(1, 2)..Point::new(3, 0), ""),
7352 (Point::new(4, 2)..Point::new(6, 0), ""),
7353 ],
7354 cx,
7355 );
7356 assert_eq!(
7357 buffer.read(cx).text(),
7358 "
7359 a
7360 b()
7361 c()
7362 "
7363 .unindent()
7364 );
7365 });
7366
7367 editor.update(cx, |editor, cx| {
7368 assert_eq!(
7369 editor.selections.ranges(cx),
7370 &[
7371 Point::new(1, 2)..Point::new(1, 2),
7372 Point::new(2, 2)..Point::new(2, 2),
7373 ],
7374 );
7375
7376 editor.newline(&Newline, cx);
7377 assert_eq!(
7378 editor.text(cx),
7379 "
7380 a
7381 b(
7382 )
7383 c(
7384 )
7385 "
7386 .unindent()
7387 );
7388
7389 // The selections are moved after the inserted newlines
7390 assert_eq!(
7391 editor.selections.ranges(cx),
7392 &[
7393 Point::new(2, 0)..Point::new(2, 0),
7394 Point::new(4, 0)..Point::new(4, 0),
7395 ],
7396 );
7397 });
7398 }
7399
7400 #[gpui::test]
7401 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7402 cx.set_global(Settings::test(cx));
7403 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7404 let (_, editor) = cx.add_window(Default::default(), |cx| {
7405 let mut editor = build_editor(buffer.clone(), cx);
7406 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7407 editor
7408 });
7409
7410 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7411 buffer.update(cx, |buffer, cx| {
7412 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7413 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7414 });
7415
7416 editor.update(cx, |editor, cx| {
7417 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7418
7419 editor.insert("Z", cx);
7420 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7421
7422 // The selections are moved after the inserted characters
7423 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7424 });
7425 }
7426
7427 #[gpui::test]
7428 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7429 let mut cx = EditorTestContext::new(cx).await;
7430
7431 cx.set_state(indoc! {"
7432 [one} [two}
7433 three
7434 four"});
7435 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7436 cx.assert_editor_state(indoc! {"
7437 [one} [two}
7438 three
7439 four"});
7440
7441 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7442 cx.assert_editor_state(indoc! {"
7443 [one} [two}
7444 three
7445 four"});
7446
7447 // select across line ending
7448 cx.set_state(indoc! {"
7449 one two
7450 t[hree
7451 } four"});
7452 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7453 cx.assert_editor_state(indoc! {"
7454 one two
7455 t[hree
7456 } four"});
7457
7458 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7459 cx.assert_editor_state(indoc! {"
7460 one two
7461 t[hree
7462 } four"});
7463
7464 // Ensure that indenting/outdenting works when the cursor is at column 0.
7465 cx.set_state(indoc! {"
7466 one two
7467 |three
7468 four"});
7469 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7470 cx.assert_editor_state(indoc! {"
7471 one two
7472 |three
7473 four"});
7474
7475 cx.set_state(indoc! {"
7476 one two
7477 | three
7478 four"});
7479 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7480 cx.assert_editor_state(indoc! {"
7481 one two
7482 |three
7483 four"});
7484 }
7485
7486 #[gpui::test]
7487 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7488 let mut cx = EditorTestContext::new(cx).await;
7489 cx.update(|cx| {
7490 cx.update_global::<Settings, _, _>(|settings, _| {
7491 settings.hard_tabs = true;
7492 });
7493 });
7494
7495 // select two ranges on one line
7496 cx.set_state(indoc! {"
7497 [one} [two}
7498 three
7499 four"});
7500 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7501 cx.assert_editor_state(indoc! {"
7502 \t[one} [two}
7503 three
7504 four"});
7505 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7506 cx.assert_editor_state(indoc! {"
7507 \t\t[one} [two}
7508 three
7509 four"});
7510 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7511 cx.assert_editor_state(indoc! {"
7512 \t[one} [two}
7513 three
7514 four"});
7515 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7516 cx.assert_editor_state(indoc! {"
7517 [one} [two}
7518 three
7519 four"});
7520
7521 // select across a line ending
7522 cx.set_state(indoc! {"
7523 one two
7524 t[hree
7525 }four"});
7526 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7527 cx.assert_editor_state(indoc! {"
7528 one two
7529 \tt[hree
7530 }four"});
7531 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7532 cx.assert_editor_state(indoc! {"
7533 one two
7534 \t\tt[hree
7535 }four"});
7536 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7537 cx.assert_editor_state(indoc! {"
7538 one two
7539 \tt[hree
7540 }four"});
7541 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7542 cx.assert_editor_state(indoc! {"
7543 one two
7544 t[hree
7545 }four"});
7546
7547 // Ensure that indenting/outdenting works when the cursor is at column 0.
7548 cx.set_state(indoc! {"
7549 one two
7550 |three
7551 four"});
7552 cx.assert_editor_state(indoc! {"
7553 one two
7554 |three
7555 four"});
7556 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7557 cx.assert_editor_state(indoc! {"
7558 one two
7559 \t|three
7560 four"});
7561 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7562 cx.assert_editor_state(indoc! {"
7563 one two
7564 |three
7565 four"});
7566 }
7567
7568 #[gpui::test]
7569 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7570 cx.set_global(
7571 Settings::test(cx)
7572 .with_overrides(
7573 "TOML",
7574 LanguageOverride {
7575 tab_size: Some(2),
7576 ..Default::default()
7577 },
7578 )
7579 .with_overrides(
7580 "Rust",
7581 LanguageOverride {
7582 tab_size: Some(4),
7583 ..Default::default()
7584 },
7585 ),
7586 );
7587 let toml_language = Arc::new(Language::new(
7588 LanguageConfig {
7589 name: "TOML".into(),
7590 ..Default::default()
7591 },
7592 None,
7593 ));
7594 let rust_language = Arc::new(Language::new(
7595 LanguageConfig {
7596 name: "Rust".into(),
7597 ..Default::default()
7598 },
7599 None,
7600 ));
7601
7602 let toml_buffer = cx
7603 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7604 let rust_buffer = cx.add_model(|cx| {
7605 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7606 });
7607 let multibuffer = cx.add_model(|cx| {
7608 let mut multibuffer = MultiBuffer::new(0);
7609 multibuffer.push_excerpts(
7610 toml_buffer.clone(),
7611 [ExcerptRange {
7612 context: Point::new(0, 0)..Point::new(2, 0),
7613 primary: None,
7614 }],
7615 cx,
7616 );
7617 multibuffer.push_excerpts(
7618 rust_buffer.clone(),
7619 [ExcerptRange {
7620 context: Point::new(0, 0)..Point::new(1, 0),
7621 primary: None,
7622 }],
7623 cx,
7624 );
7625 multibuffer
7626 });
7627
7628 cx.add_window(Default::default(), |cx| {
7629 let mut editor = build_editor(multibuffer, cx);
7630
7631 assert_eq!(
7632 editor.text(cx),
7633 indoc! {"
7634 a = 1
7635 b = 2
7636
7637 const c: usize = 3;
7638 "}
7639 );
7640
7641 select_ranges(
7642 &mut editor,
7643 indoc! {"
7644 [a] = 1
7645 b = 2
7646
7647 [const c:] usize = 3;
7648 "},
7649 cx,
7650 );
7651
7652 editor.tab(&Tab, cx);
7653 assert_text_with_selections(
7654 &mut editor,
7655 indoc! {"
7656 [a] = 1
7657 b = 2
7658
7659 [const c:] usize = 3;
7660 "},
7661 cx,
7662 );
7663 editor.tab_prev(&TabPrev, 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
7675 editor
7676 });
7677 }
7678
7679 #[gpui::test]
7680 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7681 let mut cx = EditorTestContext::new(cx).await;
7682 // Basic backspace
7683 cx.set_state(indoc! {"
7684 on|e two three
7685 fou[r} five six
7686 seven {eight nine
7687 ]ten"});
7688 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7689 cx.assert_editor_state(indoc! {"
7690 o|e two three
7691 fou| five six
7692 seven |ten"});
7693
7694 // Test backspace inside and around indents
7695 cx.set_state(indoc! {"
7696 zero
7697 |one
7698 |two
7699 | | | three
7700 | | four"});
7701 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7702 cx.assert_editor_state(indoc! {"
7703 zero
7704 |one
7705 |two
7706 | three| four"});
7707
7708 // Test backspace with line_mode set to true
7709 cx.update_editor(|e, _| e.selections.line_mode = true);
7710 cx.set_state(indoc! {"
7711 The |quick |brown
7712 fox jumps over
7713 the lazy dog
7714 |The qu[ick b}rown"});
7715 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7716 cx.assert_editor_state(indoc! {"
7717 |fox jumps over
7718 the lazy dog|"});
7719 }
7720
7721 #[gpui::test]
7722 async fn test_delete(cx: &mut gpui::TestAppContext) {
7723 let mut cx = EditorTestContext::new(cx).await;
7724
7725 cx.set_state(indoc! {"
7726 on|e two three
7727 fou[r} five six
7728 seven {eight nine
7729 ]ten"});
7730 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7731 cx.assert_editor_state(indoc! {"
7732 on| two three
7733 fou| five six
7734 seven |ten"});
7735
7736 // Test backspace with line_mode set to true
7737 cx.update_editor(|e, _| e.selections.line_mode = true);
7738 cx.set_state(indoc! {"
7739 The |quick |brown
7740 fox {jum]ps over
7741 the lazy dog
7742 |The qu[ick b}rown"});
7743 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7744 cx.assert_editor_state("|the lazy dog|");
7745 }
7746
7747 #[gpui::test]
7748 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7749 cx.set_global(Settings::test(cx));
7750 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7751 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7752 view.update(cx, |view, cx| {
7753 view.change_selections(None, cx, |s| {
7754 s.select_display_ranges([
7755 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7756 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7757 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7758 ])
7759 });
7760 view.delete_line(&DeleteLine, cx);
7761 assert_eq!(view.display_text(cx), "ghi");
7762 assert_eq!(
7763 view.selections.display_ranges(cx),
7764 vec![
7765 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7766 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7767 ]
7768 );
7769 });
7770
7771 cx.set_global(Settings::test(cx));
7772 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7773 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7774 view.update(cx, |view, cx| {
7775 view.change_selections(None, cx, |s| {
7776 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7777 });
7778 view.delete_line(&DeleteLine, cx);
7779 assert_eq!(view.display_text(cx), "ghi\n");
7780 assert_eq!(
7781 view.selections.display_ranges(cx),
7782 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7783 );
7784 });
7785 }
7786
7787 #[gpui::test]
7788 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7789 cx.set_global(Settings::test(cx));
7790 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7791 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7792 view.update(cx, |view, cx| {
7793 view.change_selections(None, cx, |s| {
7794 s.select_display_ranges([
7795 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7796 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7797 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7798 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7799 ])
7800 });
7801 view.duplicate_line(&DuplicateLine, cx);
7802 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7803 assert_eq!(
7804 view.selections.display_ranges(cx),
7805 vec![
7806 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7807 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7808 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7809 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7810 ]
7811 );
7812 });
7813
7814 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7815 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7816 view.update(cx, |view, cx| {
7817 view.change_selections(None, cx, |s| {
7818 s.select_display_ranges([
7819 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7820 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7821 ])
7822 });
7823 view.duplicate_line(&DuplicateLine, cx);
7824 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7825 assert_eq!(
7826 view.selections.display_ranges(cx),
7827 vec![
7828 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7829 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7830 ]
7831 );
7832 });
7833 }
7834
7835 #[gpui::test]
7836 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7837 cx.set_global(Settings::test(cx));
7838 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7839 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7840 view.update(cx, |view, cx| {
7841 view.fold_ranges(
7842 vec![
7843 Point::new(0, 2)..Point::new(1, 2),
7844 Point::new(2, 3)..Point::new(4, 1),
7845 Point::new(7, 0)..Point::new(8, 4),
7846 ],
7847 cx,
7848 );
7849 view.change_selections(None, cx, |s| {
7850 s.select_display_ranges([
7851 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7852 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7853 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7854 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7855 ])
7856 });
7857 assert_eq!(
7858 view.display_text(cx),
7859 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7860 );
7861
7862 view.move_line_up(&MoveLineUp, cx);
7863 assert_eq!(
7864 view.display_text(cx),
7865 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7866 );
7867 assert_eq!(
7868 view.selections.display_ranges(cx),
7869 vec![
7870 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7871 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7872 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7873 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7874 ]
7875 );
7876 });
7877
7878 view.update(cx, |view, cx| {
7879 view.move_line_down(&MoveLineDown, cx);
7880 assert_eq!(
7881 view.display_text(cx),
7882 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7883 );
7884 assert_eq!(
7885 view.selections.display_ranges(cx),
7886 vec![
7887 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7888 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7889 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7890 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7891 ]
7892 );
7893 });
7894
7895 view.update(cx, |view, cx| {
7896 view.move_line_down(&MoveLineDown, cx);
7897 assert_eq!(
7898 view.display_text(cx),
7899 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
7900 );
7901 assert_eq!(
7902 view.selections.display_ranges(cx),
7903 vec![
7904 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7905 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7906 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7907 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7908 ]
7909 );
7910 });
7911
7912 view.update(cx, |view, cx| {
7913 view.move_line_up(&MoveLineUp, cx);
7914 assert_eq!(
7915 view.display_text(cx),
7916 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
7917 );
7918 assert_eq!(
7919 view.selections.display_ranges(cx),
7920 vec![
7921 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7922 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7923 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7924 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7925 ]
7926 );
7927 });
7928 }
7929
7930 #[gpui::test]
7931 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
7932 cx.set_global(Settings::test(cx));
7933 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7934 let snapshot = buffer.read(cx).snapshot(cx);
7935 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7936 editor.update(cx, |editor, cx| {
7937 editor.insert_blocks(
7938 [BlockProperties {
7939 position: snapshot.anchor_after(Point::new(2, 0)),
7940 disposition: BlockDisposition::Below,
7941 height: 1,
7942 render: Arc::new(|_| Empty::new().boxed()),
7943 }],
7944 cx,
7945 );
7946 editor.change_selections(None, cx, |s| {
7947 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7948 });
7949 editor.move_line_down(&MoveLineDown, cx);
7950 });
7951 }
7952
7953 #[gpui::test]
7954 fn test_transpose(cx: &mut gpui::MutableAppContext) {
7955 cx.set_global(Settings::test(cx));
7956
7957 cx.add_window(Default::default(), |cx| {
7958 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
7959
7960 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
7961 editor.transpose(&Default::default(), cx);
7962 assert_eq!(editor.text(cx), "bac");
7963 assert_eq!(editor.selections.ranges(cx), [2..2]);
7964
7965 editor.transpose(&Default::default(), cx);
7966 assert_eq!(editor.text(cx), "bca");
7967 assert_eq!(editor.selections.ranges(cx), [3..3]);
7968
7969 editor.transpose(&Default::default(), cx);
7970 assert_eq!(editor.text(cx), "bac");
7971 assert_eq!(editor.selections.ranges(cx), [3..3]);
7972
7973 editor
7974 })
7975 .1;
7976
7977 cx.add_window(Default::default(), |cx| {
7978 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
7979
7980 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
7981 editor.transpose(&Default::default(), cx);
7982 assert_eq!(editor.text(cx), "acb\nde");
7983 assert_eq!(editor.selections.ranges(cx), [3..3]);
7984
7985 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
7986 editor.transpose(&Default::default(), cx);
7987 assert_eq!(editor.text(cx), "acbd\ne");
7988 assert_eq!(editor.selections.ranges(cx), [5..5]);
7989
7990 editor.transpose(&Default::default(), cx);
7991 assert_eq!(editor.text(cx), "acbde\n");
7992 assert_eq!(editor.selections.ranges(cx), [6..6]);
7993
7994 editor.transpose(&Default::default(), cx);
7995 assert_eq!(editor.text(cx), "acbd\ne");
7996 assert_eq!(editor.selections.ranges(cx), [6..6]);
7997
7998 editor
7999 })
8000 .1;
8001
8002 cx.add_window(Default::default(), |cx| {
8003 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8004
8005 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8006 editor.transpose(&Default::default(), cx);
8007 assert_eq!(editor.text(cx), "bacd\ne");
8008 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8009
8010 editor.transpose(&Default::default(), cx);
8011 assert_eq!(editor.text(cx), "bcade\n");
8012 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8013
8014 editor.transpose(&Default::default(), cx);
8015 assert_eq!(editor.text(cx), "bcda\ne");
8016 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8017
8018 editor.transpose(&Default::default(), cx);
8019 assert_eq!(editor.text(cx), "bcade\n");
8020 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8021
8022 editor.transpose(&Default::default(), cx);
8023 assert_eq!(editor.text(cx), "bcaed\n");
8024 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8025
8026 editor
8027 })
8028 .1;
8029
8030 cx.add_window(Default::default(), |cx| {
8031 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8032
8033 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8034 editor.transpose(&Default::default(), cx);
8035 assert_eq!(editor.text(cx), "🏀🍐✋");
8036 assert_eq!(editor.selections.ranges(cx), [8..8]);
8037
8038 editor.transpose(&Default::default(), cx);
8039 assert_eq!(editor.text(cx), "🏀✋🍐");
8040 assert_eq!(editor.selections.ranges(cx), [11..11]);
8041
8042 editor.transpose(&Default::default(), cx);
8043 assert_eq!(editor.text(cx), "🏀🍐✋");
8044 assert_eq!(editor.selections.ranges(cx), [11..11]);
8045
8046 editor
8047 })
8048 .1;
8049 }
8050
8051 #[gpui::test]
8052 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8053 let mut cx = EditorTestContext::new(cx).await;
8054
8055 cx.set_state("[one✅ }two [three }four [five }six ");
8056 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8057 cx.assert_editor_state("|two |four |six ");
8058
8059 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8060 cx.set_state("two |four |six |");
8061 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8062 cx.assert_editor_state("two one✅ |four three |six five |");
8063
8064 // Paste again but with only two cursors. Since the number of cursors doesn't
8065 // match the number of slices in the clipboard, the entire clipboard text
8066 // is pasted at each cursor.
8067 cx.set_state("|two one✅ four three six five |");
8068 cx.update_editor(|e, cx| {
8069 e.handle_input(&Input("( ".into()), cx);
8070 e.paste(&Paste, cx);
8071 e.handle_input(&Input(") ".into()), cx);
8072 });
8073 cx.assert_editor_state(indoc! {"
8074 ( one✅
8075 three
8076 five ) |two one✅ four three six five ( one✅
8077 three
8078 five ) |"});
8079
8080 // Cut with three selections, one of which is full-line.
8081 cx.set_state(indoc! {"
8082 1[2}3
8083 4|567
8084 [8}9"});
8085 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8086 cx.assert_editor_state(indoc! {"
8087 1|3
8088 |9"});
8089
8090 // Paste with three selections, noticing how the copied selection that was full-line
8091 // gets inserted before the second cursor.
8092 cx.set_state(indoc! {"
8093 1|3
8094 9|
8095 [o}ne"});
8096 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8097 cx.assert_editor_state(indoc! {"
8098 12|3
8099 4567
8100 9|
8101 8|ne"});
8102
8103 // Copy with a single cursor only, which writes the whole line into the clipboard.
8104 cx.set_state(indoc! {"
8105 The quick brown
8106 fox ju|mps over
8107 the lazy dog"});
8108 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8109 cx.assert_clipboard_content(Some("fox jumps over\n"));
8110
8111 // Paste with three selections, noticing how the copied full-line selection is inserted
8112 // before the empty selections but replaces the selection that is non-empty.
8113 cx.set_state(indoc! {"
8114 T|he quick brown
8115 [fo}x jumps over
8116 t|he lazy dog"});
8117 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8118 cx.assert_editor_state(indoc! {"
8119 fox jumps over
8120 T|he quick brown
8121 fox jumps over
8122 |x jumps over
8123 fox jumps over
8124 t|he lazy dog"});
8125 }
8126
8127 #[gpui::test]
8128 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8129 cx.set_global(Settings::test(cx));
8130 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8131 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8132 view.update(cx, |view, cx| {
8133 view.select_all(&SelectAll, cx);
8134 assert_eq!(
8135 view.selections.display_ranges(cx),
8136 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8137 );
8138 });
8139 }
8140
8141 #[gpui::test]
8142 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8143 cx.set_global(Settings::test(cx));
8144 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8145 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8146 view.update(cx, |view, cx| {
8147 view.change_selections(None, cx, |s| {
8148 s.select_display_ranges([
8149 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8150 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8151 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8152 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8153 ])
8154 });
8155 view.select_line(&SelectLine, cx);
8156 assert_eq!(
8157 view.selections.display_ranges(cx),
8158 vec![
8159 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8160 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8161 ]
8162 );
8163 });
8164
8165 view.update(cx, |view, cx| {
8166 view.select_line(&SelectLine, cx);
8167 assert_eq!(
8168 view.selections.display_ranges(cx),
8169 vec![
8170 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8171 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8172 ]
8173 );
8174 });
8175
8176 view.update(cx, |view, cx| {
8177 view.select_line(&SelectLine, cx);
8178 assert_eq!(
8179 view.selections.display_ranges(cx),
8180 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8181 );
8182 });
8183 }
8184
8185 #[gpui::test]
8186 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8187 cx.set_global(Settings::test(cx));
8188 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8189 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8190 view.update(cx, |view, cx| {
8191 view.fold_ranges(
8192 vec![
8193 Point::new(0, 2)..Point::new(1, 2),
8194 Point::new(2, 3)..Point::new(4, 1),
8195 Point::new(7, 0)..Point::new(8, 4),
8196 ],
8197 cx,
8198 );
8199 view.change_selections(None, cx, |s| {
8200 s.select_display_ranges([
8201 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8202 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8203 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8204 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8205 ])
8206 });
8207 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8208 });
8209
8210 view.update(cx, |view, cx| {
8211 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8212 assert_eq!(
8213 view.display_text(cx),
8214 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8215 );
8216 assert_eq!(
8217 view.selections.display_ranges(cx),
8218 [
8219 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8220 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8221 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8222 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8223 ]
8224 );
8225 });
8226
8227 view.update(cx, |view, cx| {
8228 view.change_selections(None, cx, |s| {
8229 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8230 });
8231 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8232 assert_eq!(
8233 view.display_text(cx),
8234 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8235 );
8236 assert_eq!(
8237 view.selections.display_ranges(cx),
8238 [
8239 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8240 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8241 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8242 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8243 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8244 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8245 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8246 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8247 ]
8248 );
8249 });
8250 }
8251
8252 #[gpui::test]
8253 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8254 cx.set_global(Settings::test(cx));
8255 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8256 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8257
8258 view.update(cx, |view, cx| {
8259 view.change_selections(None, cx, |s| {
8260 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8261 });
8262 });
8263 view.update(cx, |view, cx| {
8264 view.add_selection_above(&AddSelectionAbove, cx);
8265 assert_eq!(
8266 view.selections.display_ranges(cx),
8267 vec![
8268 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8269 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8270 ]
8271 );
8272 });
8273
8274 view.update(cx, |view, cx| {
8275 view.add_selection_above(&AddSelectionAbove, cx);
8276 assert_eq!(
8277 view.selections.display_ranges(cx),
8278 vec![
8279 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8280 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8281 ]
8282 );
8283 });
8284
8285 view.update(cx, |view, cx| {
8286 view.add_selection_below(&AddSelectionBelow, cx);
8287 assert_eq!(
8288 view.selections.display_ranges(cx),
8289 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8290 );
8291
8292 view.undo_selection(&UndoSelection, cx);
8293 assert_eq!(
8294 view.selections.display_ranges(cx),
8295 vec![
8296 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8297 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8298 ]
8299 );
8300
8301 view.redo_selection(&RedoSelection, cx);
8302 assert_eq!(
8303 view.selections.display_ranges(cx),
8304 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8305 );
8306 });
8307
8308 view.update(cx, |view, cx| {
8309 view.add_selection_below(&AddSelectionBelow, cx);
8310 assert_eq!(
8311 view.selections.display_ranges(cx),
8312 vec![
8313 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8314 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8315 ]
8316 );
8317 });
8318
8319 view.update(cx, |view, cx| {
8320 view.add_selection_below(&AddSelectionBelow, cx);
8321 assert_eq!(
8322 view.selections.display_ranges(cx),
8323 vec![
8324 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8325 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8326 ]
8327 );
8328 });
8329
8330 view.update(cx, |view, cx| {
8331 view.change_selections(None, cx, |s| {
8332 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8333 });
8334 });
8335 view.update(cx, |view, cx| {
8336 view.add_selection_below(&AddSelectionBelow, cx);
8337 assert_eq!(
8338 view.selections.display_ranges(cx),
8339 vec![
8340 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8341 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8342 ]
8343 );
8344 });
8345
8346 view.update(cx, |view, cx| {
8347 view.add_selection_below(&AddSelectionBelow, cx);
8348 assert_eq!(
8349 view.selections.display_ranges(cx),
8350 vec![
8351 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8352 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8353 ]
8354 );
8355 });
8356
8357 view.update(cx, |view, cx| {
8358 view.add_selection_above(&AddSelectionAbove, cx);
8359 assert_eq!(
8360 view.selections.display_ranges(cx),
8361 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8362 );
8363 });
8364
8365 view.update(cx, |view, cx| {
8366 view.add_selection_above(&AddSelectionAbove, cx);
8367 assert_eq!(
8368 view.selections.display_ranges(cx),
8369 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8370 );
8371 });
8372
8373 view.update(cx, |view, cx| {
8374 view.change_selections(None, cx, |s| {
8375 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8376 });
8377 view.add_selection_below(&AddSelectionBelow, cx);
8378 assert_eq!(
8379 view.selections.display_ranges(cx),
8380 vec![
8381 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8382 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8383 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8384 ]
8385 );
8386 });
8387
8388 view.update(cx, |view, cx| {
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 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8397 ]
8398 );
8399 });
8400
8401 view.update(cx, |view, cx| {
8402 view.add_selection_above(&AddSelectionAbove, cx);
8403 assert_eq!(
8404 view.selections.display_ranges(cx),
8405 vec![
8406 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8407 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8408 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8409 ]
8410 );
8411 });
8412
8413 view.update(cx, |view, cx| {
8414 view.change_selections(None, cx, |s| {
8415 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8416 });
8417 });
8418 view.update(cx, |view, cx| {
8419 view.add_selection_above(&AddSelectionAbove, cx);
8420 assert_eq!(
8421 view.selections.display_ranges(cx),
8422 vec![
8423 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8424 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8425 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8426 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8427 ]
8428 );
8429 });
8430
8431 view.update(cx, |view, cx| {
8432 view.add_selection_below(&AddSelectionBelow, cx);
8433 assert_eq!(
8434 view.selections.display_ranges(cx),
8435 vec![
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
8444 #[gpui::test]
8445 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8446 cx.set_global(Settings::test(cx));
8447
8448 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8449 let buffer = MultiBuffer::build_simple(&text, cx);
8450 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8451
8452 view.update(cx, |view, cx| {
8453 view.change_selections(None, cx, |s| {
8454 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8455 });
8456 view.select_next(
8457 &SelectNext {
8458 replace_newest: false,
8459 },
8460 cx,
8461 );
8462 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8463
8464 view.select_next(
8465 &SelectNext {
8466 replace_newest: false,
8467 },
8468 cx,
8469 );
8470 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8471
8472 view.undo_selection(&UndoSelection, cx);
8473 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8474
8475 view.redo_selection(&RedoSelection, cx);
8476 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8477
8478 view.select_next(
8479 &SelectNext {
8480 replace_newest: false,
8481 },
8482 cx,
8483 );
8484 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8485
8486 view.select_next(
8487 &SelectNext {
8488 replace_newest: false,
8489 },
8490 cx,
8491 );
8492 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8493 });
8494 }
8495
8496 #[gpui::test]
8497 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8498 cx.update(|cx| cx.set_global(Settings::test(cx)));
8499 let language = Arc::new(Language::new(
8500 LanguageConfig::default(),
8501 Some(tree_sitter_rust::language()),
8502 ));
8503
8504 let text = r#"
8505 use mod1::mod2::{mod3, mod4};
8506
8507 fn fn_1(param1: bool, param2: &str) {
8508 let var1 = "text";
8509 }
8510 "#
8511 .unindent();
8512
8513 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8514 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8515 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8516 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8517 .await;
8518
8519 view.update(cx, |view, cx| {
8520 view.change_selections(None, cx, |s| {
8521 s.select_display_ranges([
8522 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8523 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8524 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8525 ]);
8526 });
8527 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8528 });
8529 assert_eq!(
8530 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8531 &[
8532 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8533 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8534 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8535 ]
8536 );
8537
8538 view.update(cx, |view, cx| {
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, 16)..DisplayPoint::new(0, 28),
8545 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8546 ]
8547 );
8548
8549 view.update(cx, |view, cx| {
8550 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8551 });
8552 assert_eq!(
8553 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8554 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8555 );
8556
8557 // Trying to expand the selected syntax node one more time has no effect.
8558 view.update(cx, |view, cx| {
8559 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8560 });
8561 assert_eq!(
8562 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8563 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8564 );
8565
8566 view.update(cx, |view, cx| {
8567 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8568 });
8569 assert_eq!(
8570 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8571 &[
8572 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8573 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8574 ]
8575 );
8576
8577 view.update(cx, |view, cx| {
8578 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8579 });
8580 assert_eq!(
8581 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8582 &[
8583 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8584 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8585 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
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, 25)..DisplayPoint::new(0, 25),
8596 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8597 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8598 ]
8599 );
8600
8601 // Trying to shrink the selected syntax node one more time has no effect.
8602 view.update(cx, |view, cx| {
8603 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8604 });
8605 assert_eq!(
8606 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8607 &[
8608 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8609 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8610 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8611 ]
8612 );
8613
8614 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8615 // a fold.
8616 view.update(cx, |view, cx| {
8617 view.fold_ranges(
8618 vec![
8619 Point::new(0, 21)..Point::new(0, 24),
8620 Point::new(3, 20)..Point::new(3, 22),
8621 ],
8622 cx,
8623 );
8624 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8625 });
8626 assert_eq!(
8627 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8628 &[
8629 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8630 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8631 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8632 ]
8633 );
8634 }
8635
8636 #[gpui::test]
8637 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8638 cx.update(|cx| cx.set_global(Settings::test(cx)));
8639 let language = Arc::new(
8640 Language::new(
8641 LanguageConfig {
8642 brackets: vec![
8643 BracketPair {
8644 start: "{".to_string(),
8645 end: "}".to_string(),
8646 close: false,
8647 newline: true,
8648 },
8649 BracketPair {
8650 start: "(".to_string(),
8651 end: ")".to_string(),
8652 close: false,
8653 newline: true,
8654 },
8655 ],
8656 ..Default::default()
8657 },
8658 Some(tree_sitter_rust::language()),
8659 )
8660 .with_indents_query(
8661 r#"
8662 (_ "(" ")" @end) @indent
8663 (_ "{" "}" @end) @indent
8664 "#,
8665 )
8666 .unwrap(),
8667 );
8668
8669 let text = "fn a() {}";
8670
8671 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8672 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8673 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8674 editor
8675 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8676 .await;
8677
8678 editor.update(cx, |editor, cx| {
8679 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8680 editor.newline(&Newline, cx);
8681 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8682 assert_eq!(
8683 editor.selections.ranges(cx),
8684 &[
8685 Point::new(1, 4)..Point::new(1, 4),
8686 Point::new(3, 4)..Point::new(3, 4),
8687 Point::new(5, 0)..Point::new(5, 0)
8688 ]
8689 );
8690 });
8691 }
8692
8693 #[gpui::test]
8694 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8695 cx.update(|cx| cx.set_global(Settings::test(cx)));
8696 let language = Arc::new(Language::new(
8697 LanguageConfig {
8698 brackets: vec![
8699 BracketPair {
8700 start: "{".to_string(),
8701 end: "}".to_string(),
8702 close: true,
8703 newline: true,
8704 },
8705 BracketPair {
8706 start: "/*".to_string(),
8707 end: " */".to_string(),
8708 close: true,
8709 newline: true,
8710 },
8711 ],
8712 autoclose_before: "})]".to_string(),
8713 ..Default::default()
8714 },
8715 Some(tree_sitter_rust::language()),
8716 ));
8717
8718 let text = r#"
8719 a
8720
8721 /
8722
8723 "#
8724 .unindent();
8725
8726 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8727 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8728 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8729 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8730 .await;
8731
8732 view.update(cx, |view, cx| {
8733 view.change_selections(None, cx, |s| {
8734 s.select_display_ranges([
8735 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8736 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8737 ])
8738 });
8739
8740 view.handle_input(&Input("{".to_string()), cx);
8741 view.handle_input(&Input("{".to_string()), cx);
8742 view.handle_input(&Input("{".to_string()), cx);
8743 assert_eq!(
8744 view.text(cx),
8745 "
8746 {{{}}}
8747 {{{}}}
8748 /
8749
8750 "
8751 .unindent()
8752 );
8753
8754 view.move_right(&MoveRight, cx);
8755 view.handle_input(&Input("}".to_string()), cx);
8756 view.handle_input(&Input("}".to_string()), cx);
8757 view.handle_input(&Input("}".to_string()), cx);
8758 assert_eq!(
8759 view.text(cx),
8760 "
8761 {{{}}}}
8762 {{{}}}}
8763 /
8764
8765 "
8766 .unindent()
8767 );
8768
8769 view.undo(&Undo, cx);
8770 view.handle_input(&Input("/".to_string()), cx);
8771 view.handle_input(&Input("*".to_string()), cx);
8772 assert_eq!(
8773 view.text(cx),
8774 "
8775 /* */
8776 /* */
8777 /
8778
8779 "
8780 .unindent()
8781 );
8782
8783 view.undo(&Undo, cx);
8784 view.change_selections(None, cx, |s| {
8785 s.select_display_ranges([
8786 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8787 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8788 ])
8789 });
8790 view.handle_input(&Input("*".to_string()), cx);
8791 assert_eq!(
8792 view.text(cx),
8793 "
8794 a
8795
8796 /*
8797 *
8798 "
8799 .unindent()
8800 );
8801
8802 // Don't autoclose if the next character isn't whitespace and isn't
8803 // listed in the language's "autoclose_before" section.
8804 view.finalize_last_transaction(cx);
8805 view.change_selections(None, cx, |s| {
8806 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
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 view.undo(&Undo, cx);
8821 view.change_selections(None, cx, |s| {
8822 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8823 });
8824 view.handle_input(&Input("{".to_string()), cx);
8825 assert_eq!(
8826 view.text(cx),
8827 "
8828 {a}
8829
8830 /*
8831 *
8832 "
8833 .unindent()
8834 );
8835 assert_eq!(
8836 view.selections.display_ranges(cx),
8837 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8838 );
8839 });
8840 }
8841
8842 #[gpui::test]
8843 async fn test_snippets(cx: &mut gpui::TestAppContext) {
8844 cx.update(|cx| cx.set_global(Settings::test(cx)));
8845
8846 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
8847 a.| b
8848 a.| b
8849 a.| b"});
8850 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
8851 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8852
8853 editor.update(cx, |editor, cx| {
8854 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
8855
8856 editor
8857 .insert_snippet(&insertion_ranges, snippet, cx)
8858 .unwrap();
8859
8860 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
8861 let range_markers = ('<', '>');
8862 let (expected_text, mut selection_ranges_lookup) =
8863 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
8864 let selection_ranges = selection_ranges_lookup
8865 .remove(&range_markers.into())
8866 .unwrap();
8867 assert_eq!(editor.text(cx), expected_text);
8868 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
8869 }
8870 assert(
8871 editor,
8872 cx,
8873 indoc! {"
8874 a.f(<one>, two, <three>) b
8875 a.f(<one>, two, <three>) b
8876 a.f(<one>, two, <three>) b"},
8877 );
8878
8879 // Can't move earlier than the first tab stop
8880 assert!(!editor.move_to_prev_snippet_tabstop(cx));
8881 assert(
8882 editor,
8883 cx,
8884 indoc! {"
8885 a.f(<one>, two, <three>) b
8886 a.f(<one>, two, <three>) b
8887 a.f(<one>, two, <three>) b"},
8888 );
8889
8890 assert!(editor.move_to_next_snippet_tabstop(cx));
8891 assert(
8892 editor,
8893 cx,
8894 indoc! {"
8895 a.f(one, <two>, three) b
8896 a.f(one, <two>, three) b
8897 a.f(one, <two>, three) b"},
8898 );
8899
8900 editor.move_to_prev_snippet_tabstop(cx);
8901 assert(
8902 editor,
8903 cx,
8904 indoc! {"
8905 a.f(<one>, two, <three>) b
8906 a.f(<one>, two, <three>) b
8907 a.f(<one>, two, <three>) b"},
8908 );
8909
8910 assert!(editor.move_to_next_snippet_tabstop(cx));
8911 assert(
8912 editor,
8913 cx,
8914 indoc! {"
8915 a.f(one, <two>, three) b
8916 a.f(one, <two>, three) b
8917 a.f(one, <two>, three) b"},
8918 );
8919 assert!(editor.move_to_next_snippet_tabstop(cx));
8920 assert(
8921 editor,
8922 cx,
8923 indoc! {"
8924 a.f(one, two, three)<> b
8925 a.f(one, two, three)<> b
8926 a.f(one, two, three)<> b"},
8927 );
8928
8929 // As soon as the last tab stop is reached, snippet state is gone
8930 editor.move_to_prev_snippet_tabstop(cx);
8931 assert(
8932 editor,
8933 cx,
8934 indoc! {"
8935 a.f(one, two, three)<> b
8936 a.f(one, two, three)<> b
8937 a.f(one, two, three)<> b"},
8938 );
8939 });
8940 }
8941
8942 #[gpui::test]
8943 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
8944 cx.foreground().forbid_parking();
8945
8946 let mut language = Language::new(
8947 LanguageConfig {
8948 name: "Rust".into(),
8949 path_suffixes: vec!["rs".to_string()],
8950 ..Default::default()
8951 },
8952 Some(tree_sitter_rust::language()),
8953 );
8954 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
8955 capabilities: lsp::ServerCapabilities {
8956 document_formatting_provider: Some(lsp::OneOf::Left(true)),
8957 ..Default::default()
8958 },
8959 ..Default::default()
8960 });
8961
8962 let fs = FakeFs::new(cx.background().clone());
8963 fs.insert_file("/file.rs", Default::default()).await;
8964
8965 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8966 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
8967 let buffer = project
8968 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8969 .await
8970 .unwrap();
8971
8972 cx.foreground().start_waiting();
8973 let fake_server = fake_servers.next().await.unwrap();
8974
8975 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8976 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8977 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
8978 assert!(cx.read(|cx| editor.is_dirty(cx)));
8979
8980 let save = cx.update(|cx| editor.save(project.clone(), cx));
8981 fake_server
8982 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
8983 assert_eq!(
8984 params.text_document.uri,
8985 lsp::Url::from_file_path("/file.rs").unwrap()
8986 );
8987 assert_eq!(params.options.tab_size, 4);
8988 Ok(Some(vec![lsp::TextEdit::new(
8989 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
8990 ", ".to_string(),
8991 )]))
8992 })
8993 .next()
8994 .await;
8995 cx.foreground().start_waiting();
8996 save.await.unwrap();
8997 assert_eq!(
8998 editor.read_with(cx, |editor, cx| editor.text(cx)),
8999 "one, two\nthree\n"
9000 );
9001 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9002
9003 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9004 assert!(cx.read(|cx| editor.is_dirty(cx)));
9005
9006 // Ensure we can still save even if formatting hangs.
9007 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9008 assert_eq!(
9009 params.text_document.uri,
9010 lsp::Url::from_file_path("/file.rs").unwrap()
9011 );
9012 futures::future::pending::<()>().await;
9013 unreachable!()
9014 });
9015 let save = cx.update(|cx| editor.save(project.clone(), cx));
9016 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9017 cx.foreground().start_waiting();
9018 save.await.unwrap();
9019 assert_eq!(
9020 editor.read_with(cx, |editor, cx| editor.text(cx)),
9021 "one\ntwo\nthree\n"
9022 );
9023 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9024
9025 // Set rust language override and assert overriden tabsize is sent to language server
9026 cx.update(|cx| {
9027 cx.update_global::<Settings, _, _>(|settings, _| {
9028 settings.language_overrides.insert(
9029 "Rust".into(),
9030 LanguageOverride {
9031 tab_size: Some(8),
9032 ..Default::default()
9033 },
9034 );
9035 })
9036 });
9037
9038 let save = cx.update(|cx| editor.save(project.clone(), cx));
9039 fake_server
9040 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9041 assert_eq!(
9042 params.text_document.uri,
9043 lsp::Url::from_file_path("/file.rs").unwrap()
9044 );
9045 assert_eq!(params.options.tab_size, 8);
9046 Ok(Some(vec![]))
9047 })
9048 .next()
9049 .await;
9050 cx.foreground().start_waiting();
9051 save.await.unwrap();
9052 }
9053
9054 #[gpui::test]
9055 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9056 cx.foreground().forbid_parking();
9057
9058 let mut language = Language::new(
9059 LanguageConfig {
9060 name: "Rust".into(),
9061 path_suffixes: vec!["rs".to_string()],
9062 ..Default::default()
9063 },
9064 Some(tree_sitter_rust::language()),
9065 );
9066 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9067 capabilities: lsp::ServerCapabilities {
9068 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9069 ..Default::default()
9070 },
9071 ..Default::default()
9072 });
9073
9074 let fs = FakeFs::new(cx.background().clone());
9075 fs.insert_file("/file.rs", Default::default()).await;
9076
9077 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9078 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9079 let buffer = project
9080 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9081 .await
9082 .unwrap();
9083
9084 cx.foreground().start_waiting();
9085 let fake_server = fake_servers.next().await.unwrap();
9086
9087 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9088 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9089 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9090 assert!(cx.read(|cx| editor.is_dirty(cx)));
9091
9092 let save = cx.update(|cx| editor.save(project.clone(), cx));
9093 fake_server
9094 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9095 assert_eq!(
9096 params.text_document.uri,
9097 lsp::Url::from_file_path("/file.rs").unwrap()
9098 );
9099 assert_eq!(params.options.tab_size, 4);
9100 Ok(Some(vec![lsp::TextEdit::new(
9101 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9102 ", ".to_string(),
9103 )]))
9104 })
9105 .next()
9106 .await;
9107 cx.foreground().start_waiting();
9108 save.await.unwrap();
9109 assert_eq!(
9110 editor.read_with(cx, |editor, cx| editor.text(cx)),
9111 "one, two\nthree\n"
9112 );
9113 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9114
9115 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9116 assert!(cx.read(|cx| editor.is_dirty(cx)));
9117
9118 // Ensure we can still save even if formatting hangs.
9119 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9120 move |params, _| async move {
9121 assert_eq!(
9122 params.text_document.uri,
9123 lsp::Url::from_file_path("/file.rs").unwrap()
9124 );
9125 futures::future::pending::<()>().await;
9126 unreachable!()
9127 },
9128 );
9129 let save = cx.update(|cx| editor.save(project.clone(), cx));
9130 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9131 cx.foreground().start_waiting();
9132 save.await.unwrap();
9133 assert_eq!(
9134 editor.read_with(cx, |editor, cx| editor.text(cx)),
9135 "one\ntwo\nthree\n"
9136 );
9137 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9138
9139 // Set rust language override and assert overriden tabsize is sent to language server
9140 cx.update(|cx| {
9141 cx.update_global::<Settings, _, _>(|settings, _| {
9142 settings.language_overrides.insert(
9143 "Rust".into(),
9144 LanguageOverride {
9145 tab_size: Some(8),
9146 ..Default::default()
9147 },
9148 );
9149 })
9150 });
9151
9152 let save = cx.update(|cx| editor.save(project.clone(), cx));
9153 fake_server
9154 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9155 assert_eq!(
9156 params.text_document.uri,
9157 lsp::Url::from_file_path("/file.rs").unwrap()
9158 );
9159 assert_eq!(params.options.tab_size, 8);
9160 Ok(Some(vec![]))
9161 })
9162 .next()
9163 .await;
9164 cx.foreground().start_waiting();
9165 save.await.unwrap();
9166 }
9167
9168 #[gpui::test]
9169 async fn test_completion(cx: &mut gpui::TestAppContext) {
9170 let mut language = Language::new(
9171 LanguageConfig {
9172 name: "Rust".into(),
9173 path_suffixes: vec!["rs".to_string()],
9174 ..Default::default()
9175 },
9176 Some(tree_sitter_rust::language()),
9177 );
9178 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9179 capabilities: lsp::ServerCapabilities {
9180 completion_provider: Some(lsp::CompletionOptions {
9181 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9182 ..Default::default()
9183 }),
9184 ..Default::default()
9185 },
9186 ..Default::default()
9187 });
9188
9189 let text = "
9190 one
9191 two
9192 three
9193 "
9194 .unindent();
9195
9196 let fs = FakeFs::new(cx.background().clone());
9197 fs.insert_file("/file.rs", text).await;
9198
9199 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9200 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9201 let buffer = project
9202 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9203 .await
9204 .unwrap();
9205 let mut fake_server = fake_servers.next().await.unwrap();
9206
9207 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9208 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9209
9210 editor.update(cx, |editor, cx| {
9211 editor.project = Some(project);
9212 editor.change_selections(None, cx, |s| {
9213 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9214 });
9215 editor.handle_input(&Input(".".to_string()), cx);
9216 });
9217
9218 handle_completion_request(
9219 &mut fake_server,
9220 "/file.rs",
9221 Point::new(0, 4),
9222 vec![
9223 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9224 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9225 ],
9226 )
9227 .await;
9228 editor
9229 .condition(&cx, |editor, _| editor.context_menu_visible())
9230 .await;
9231
9232 let apply_additional_edits = editor.update(cx, |editor, cx| {
9233 editor.move_down(&MoveDown, cx);
9234 let apply_additional_edits = editor
9235 .confirm_completion(&ConfirmCompletion::default(), cx)
9236 .unwrap();
9237 assert_eq!(
9238 editor.text(cx),
9239 "
9240 one.second_completion
9241 two
9242 three
9243 "
9244 .unindent()
9245 );
9246 apply_additional_edits
9247 });
9248
9249 handle_resolve_completion_request(
9250 &mut fake_server,
9251 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9252 )
9253 .await;
9254 apply_additional_edits.await.unwrap();
9255 assert_eq!(
9256 editor.read_with(cx, |editor, cx| editor.text(cx)),
9257 "
9258 one.second_completion
9259 two
9260 three
9261 additional edit
9262 "
9263 .unindent()
9264 );
9265
9266 editor.update(cx, |editor, cx| {
9267 editor.change_selections(None, cx, |s| {
9268 s.select_ranges([
9269 Point::new(1, 3)..Point::new(1, 3),
9270 Point::new(2, 5)..Point::new(2, 5),
9271 ])
9272 });
9273
9274 editor.handle_input(&Input(" ".to_string()), cx);
9275 assert!(editor.context_menu.is_none());
9276 editor.handle_input(&Input("s".to_string()), cx);
9277 assert!(editor.context_menu.is_none());
9278 });
9279
9280 handle_completion_request(
9281 &mut fake_server,
9282 "/file.rs",
9283 Point::new(2, 7),
9284 vec![
9285 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9286 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9287 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9288 ],
9289 )
9290 .await;
9291 editor
9292 .condition(&cx, |editor, _| editor.context_menu_visible())
9293 .await;
9294
9295 editor.update(cx, |editor, cx| {
9296 editor.handle_input(&Input("i".to_string()), cx);
9297 });
9298
9299 handle_completion_request(
9300 &mut fake_server,
9301 "/file.rs",
9302 Point::new(2, 8),
9303 vec![
9304 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9305 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9306 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9307 ],
9308 )
9309 .await;
9310 editor
9311 .condition(&cx, |editor, _| editor.context_menu_visible())
9312 .await;
9313
9314 let apply_additional_edits = editor.update(cx, |editor, cx| {
9315 let apply_additional_edits = editor
9316 .confirm_completion(&ConfirmCompletion::default(), cx)
9317 .unwrap();
9318 assert_eq!(
9319 editor.text(cx),
9320 "
9321 one.second_completion
9322 two sixth_completion
9323 three sixth_completion
9324 additional edit
9325 "
9326 .unindent()
9327 );
9328 apply_additional_edits
9329 });
9330 handle_resolve_completion_request(&mut fake_server, None).await;
9331 apply_additional_edits.await.unwrap();
9332
9333 async fn handle_completion_request(
9334 fake: &mut FakeLanguageServer,
9335 path: &'static str,
9336 position: Point,
9337 completions: Vec<(Range<Point>, &'static str)>,
9338 ) {
9339 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9340 let completions = completions.clone();
9341 async move {
9342 assert_eq!(
9343 params.text_document_position.text_document.uri,
9344 lsp::Url::from_file_path(path).unwrap()
9345 );
9346 assert_eq!(
9347 params.text_document_position.position,
9348 lsp::Position::new(position.row, position.column)
9349 );
9350 Ok(Some(lsp::CompletionResponse::Array(
9351 completions
9352 .iter()
9353 .map(|(range, new_text)| lsp::CompletionItem {
9354 label: new_text.to_string(),
9355 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9356 range: lsp::Range::new(
9357 lsp::Position::new(range.start.row, range.start.column),
9358 lsp::Position::new(range.start.row, range.start.column),
9359 ),
9360 new_text: new_text.to_string(),
9361 })),
9362 ..Default::default()
9363 })
9364 .collect(),
9365 )))
9366 }
9367 })
9368 .next()
9369 .await;
9370 }
9371
9372 async fn handle_resolve_completion_request(
9373 fake: &mut FakeLanguageServer,
9374 edit: Option<(Range<Point>, &'static str)>,
9375 ) {
9376 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9377 let edit = edit.clone();
9378 async move {
9379 Ok(lsp::CompletionItem {
9380 additional_text_edits: edit.map(|(range, new_text)| {
9381 vec![lsp::TextEdit::new(
9382 lsp::Range::new(
9383 lsp::Position::new(range.start.row, range.start.column),
9384 lsp::Position::new(range.end.row, range.end.column),
9385 ),
9386 new_text.to_string(),
9387 )]
9388 }),
9389 ..Default::default()
9390 })
9391 }
9392 })
9393 .next()
9394 .await;
9395 }
9396 }
9397
9398 #[gpui::test]
9399 async fn test_hover_popover(cx: &mut gpui::TestAppContext) {
9400 let mut cx = EditorLspTestContext::new_rust(
9401 lsp::ServerCapabilities {
9402 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
9403 ..Default::default()
9404 },
9405 cx,
9406 )
9407 .await;
9408
9409 // Basic hover delays and then pops without moving the mouse
9410 cx.set_state(indoc! {"
9411 fn |test()
9412 println!();"});
9413 let hover_point = cx.display_point(indoc! {"
9414 fn test()
9415 print|ln!();"});
9416
9417 cx.update_editor(|editor, cx| {
9418 hover_at(
9419 editor,
9420 &HoverAt {
9421 point: Some(hover_point),
9422 },
9423 cx,
9424 )
9425 });
9426 assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
9427
9428 // After delay, hover should be visible.
9429 let symbol_range = cx.lsp_range(indoc! {"
9430 fn test()
9431 [println!]();"});
9432 let mut requests =
9433 cx.lsp
9434 .handle_request::<lsp::request::HoverRequest, _, _>(move |_, _| async move {
9435 Ok(Some(lsp::Hover {
9436 contents: lsp::HoverContents::Markup(lsp::MarkupContent {
9437 kind: lsp::MarkupKind::Markdown,
9438 value: indoc! {"
9439 # Some basic docs
9440 Some test documentation"}
9441 .to_string(),
9442 }),
9443 range: Some(symbol_range),
9444 }))
9445 });
9446 cx.foreground()
9447 .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
9448 requests.next().await;
9449
9450 cx.editor(|editor, _| {
9451 assert!(editor.hover_state.visible());
9452 assert_eq!(
9453 editor.hover_state.popover.clone().unwrap().contents,
9454 vec![
9455 HoverBlock {
9456 text: "Some basic docs".to_string(),
9457 language: None
9458 },
9459 HoverBlock {
9460 text: "Some test documentation".to_string(),
9461 language: None
9462 }
9463 ]
9464 )
9465 });
9466
9467 // Mouse moved with no hover response dismisses
9468 let hover_point = cx.display_point(indoc! {"
9469 fn te|st()
9470 println!();"});
9471 cx.update_editor(|editor, cx| {
9472 hover_at(
9473 editor,
9474 &HoverAt {
9475 point: Some(hover_point),
9476 },
9477 cx,
9478 )
9479 });
9480 cx.lsp
9481 .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) })
9482 .next()
9483 .await;
9484 cx.foreground().run_until_parked();
9485 cx.editor(|editor, _| {
9486 assert!(!editor.hover_state.visible());
9487 });
9488 cx.foreground()
9489 .advance_clock(Duration::from_millis(HOVER_GRACE_MILLIS + 100));
9490
9491 // Hover with keyboard has no delay
9492 cx.set_state(indoc! {"
9493 f|n test()
9494 println!();"});
9495 cx.update_editor(|editor, cx| hover(editor, &hover_popover::Hover, cx));
9496 let symbol_range = cx.lsp_range(indoc! {"
9497 [fn] test()
9498 println!();"});
9499 cx.lsp
9500 .handle_request::<lsp::request::HoverRequest, _, _>(move |_, _| async move {
9501 Ok(Some(lsp::Hover {
9502 contents: lsp::HoverContents::Markup(lsp::MarkupContent {
9503 kind: lsp::MarkupKind::Markdown,
9504 value: indoc! {"
9505 # Some other basic docs
9506 Some other test documentation"}
9507 .to_string(),
9508 }),
9509 range: Some(symbol_range),
9510 }))
9511 })
9512 .next()
9513 .await;
9514 cx.foreground().run_until_parked();
9515 cx.editor(|editor, _| {
9516 assert!(editor.hover_state.visible());
9517 assert_eq!(
9518 editor.hover_state.popover.clone().unwrap().contents,
9519 vec![
9520 HoverBlock {
9521 text: "Some other basic docs".to_string(),
9522 language: None
9523 },
9524 HoverBlock {
9525 text: "Some other test documentation".to_string(),
9526 language: None
9527 }
9528 ]
9529 )
9530 });
9531
9532 // Open hover popover disables delay
9533 let hover_point = cx.display_point(indoc! {"
9534 fn test()
9535 print|ln!();"});
9536 cx.update_editor(|editor, cx| {
9537 hover_at(
9538 editor,
9539 &HoverAt {
9540 point: Some(hover_point),
9541 },
9542 cx,
9543 )
9544 });
9545
9546 let symbol_range = cx.lsp_range(indoc! {"
9547 fn test()
9548 [println!]();"});
9549 cx.lsp
9550 .handle_request::<lsp::request::HoverRequest, _, _>(move |_, _| async move {
9551 Ok(Some(lsp::Hover {
9552 contents: lsp::HoverContents::Markup(lsp::MarkupContent {
9553 kind: lsp::MarkupKind::Markdown,
9554 value: indoc! {"
9555 # Some third basic docs
9556 Some third test documentation"}
9557 .to_string(),
9558 }),
9559 range: Some(symbol_range),
9560 }))
9561 })
9562 .next()
9563 .await;
9564 cx.foreground().run_until_parked();
9565 // No delay as the popover is already visible
9566
9567 cx.editor(|editor, _| {
9568 assert!(editor.hover_state.visible());
9569 assert_eq!(
9570 editor.hover_state.popover.clone().unwrap().contents,
9571 vec![
9572 HoverBlock {
9573 text: "Some third basic docs".to_string(),
9574 language: None
9575 },
9576 HoverBlock {
9577 text: "Some third test documentation".to_string(),
9578 language: None
9579 }
9580 ]
9581 )
9582 });
9583 }
9584
9585 #[gpui::test]
9586 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9587 cx.update(|cx| cx.set_global(Settings::test(cx)));
9588 let language = Arc::new(Language::new(
9589 LanguageConfig {
9590 line_comment: Some("// ".to_string()),
9591 ..Default::default()
9592 },
9593 Some(tree_sitter_rust::language()),
9594 ));
9595
9596 let text = "
9597 fn a() {
9598 //b();
9599 // c();
9600 // d();
9601 }
9602 "
9603 .unindent();
9604
9605 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9606 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9607 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9608
9609 view.update(cx, |editor, cx| {
9610 // If multiple selections intersect a line, the line is only
9611 // toggled once.
9612 editor.change_selections(None, cx, |s| {
9613 s.select_display_ranges([
9614 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9615 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9616 ])
9617 });
9618 editor.toggle_comments(&ToggleComments, cx);
9619 assert_eq!(
9620 editor.text(cx),
9621 "
9622 fn a() {
9623 b();
9624 c();
9625 d();
9626 }
9627 "
9628 .unindent()
9629 );
9630
9631 // The comment prefix is inserted at the same column for every line
9632 // in a selection.
9633 editor.change_selections(None, cx, |s| {
9634 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9635 });
9636 editor.toggle_comments(&ToggleComments, cx);
9637 assert_eq!(
9638 editor.text(cx),
9639 "
9640 fn a() {
9641 // b();
9642 // c();
9643 // d();
9644 }
9645 "
9646 .unindent()
9647 );
9648
9649 // If a selection ends at the beginning of a line, that line is not toggled.
9650 editor.change_selections(None, cx, |s| {
9651 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9652 });
9653 editor.toggle_comments(&ToggleComments, cx);
9654 assert_eq!(
9655 editor.text(cx),
9656 "
9657 fn a() {
9658 // b();
9659 c();
9660 // d();
9661 }
9662 "
9663 .unindent()
9664 );
9665 });
9666 }
9667
9668 #[gpui::test]
9669 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9670 cx.set_global(Settings::test(cx));
9671 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9672 let multibuffer = cx.add_model(|cx| {
9673 let mut multibuffer = MultiBuffer::new(0);
9674 multibuffer.push_excerpts(
9675 buffer.clone(),
9676 [
9677 ExcerptRange {
9678 context: Point::new(0, 0)..Point::new(0, 4),
9679 primary: None,
9680 },
9681 ExcerptRange {
9682 context: Point::new(1, 0)..Point::new(1, 4),
9683 primary: None,
9684 },
9685 ],
9686 cx,
9687 );
9688 multibuffer
9689 });
9690
9691 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9692
9693 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9694 view.update(cx, |view, cx| {
9695 assert_eq!(view.text(cx), "aaaa\nbbbb");
9696 view.change_selections(None, cx, |s| {
9697 s.select_ranges([
9698 Point::new(0, 0)..Point::new(0, 0),
9699 Point::new(1, 0)..Point::new(1, 0),
9700 ])
9701 });
9702
9703 view.handle_input(&Input("X".to_string()), cx);
9704 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9705 assert_eq!(
9706 view.selections.ranges(cx),
9707 [
9708 Point::new(0, 1)..Point::new(0, 1),
9709 Point::new(1, 1)..Point::new(1, 1),
9710 ]
9711 )
9712 });
9713 }
9714
9715 #[gpui::test]
9716 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9717 cx.set_global(Settings::test(cx));
9718 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9719 [aaaa
9720 (bbbb]
9721 cccc)"});
9722 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
9723 context,
9724 primary: None,
9725 });
9726 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9727 let multibuffer = cx.add_model(|cx| {
9728 let mut multibuffer = MultiBuffer::new(0);
9729 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9730 multibuffer
9731 });
9732
9733 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9734 view.update(cx, |view, cx| {
9735 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9736 aaaa
9737 b|bbb
9738 b|bb|b
9739 cccc"});
9740 assert_eq!(view.text(cx), expected_text);
9741 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9742
9743 view.handle_input(&Input("X".to_string()), cx);
9744
9745 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9746 aaaa
9747 bX|bbXb
9748 bX|bbX|b
9749 cccc"});
9750 assert_eq!(view.text(cx), expected_text);
9751 assert_eq!(view.selections.ranges(cx), expected_selections);
9752
9753 view.newline(&Newline, cx);
9754 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9755 aaaa
9756 bX
9757 |bbX
9758 b
9759 bX
9760 |bbX
9761 |b
9762 cccc"});
9763 assert_eq!(view.text(cx), expected_text);
9764 assert_eq!(view.selections.ranges(cx), expected_selections);
9765 });
9766 }
9767
9768 #[gpui::test]
9769 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9770 cx.set_global(Settings::test(cx));
9771 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9772 let mut excerpt1_id = None;
9773 let multibuffer = cx.add_model(|cx| {
9774 let mut multibuffer = MultiBuffer::new(0);
9775 excerpt1_id = multibuffer
9776 .push_excerpts(
9777 buffer.clone(),
9778 [
9779 ExcerptRange {
9780 context: Point::new(0, 0)..Point::new(1, 4),
9781 primary: None,
9782 },
9783 ExcerptRange {
9784 context: Point::new(1, 0)..Point::new(2, 4),
9785 primary: None,
9786 },
9787 ],
9788 cx,
9789 )
9790 .into_iter()
9791 .next();
9792 multibuffer
9793 });
9794 assert_eq!(
9795 multibuffer.read(cx).read(cx).text(),
9796 "aaaa\nbbbb\nbbbb\ncccc"
9797 );
9798 let (_, editor) = cx.add_window(Default::default(), |cx| {
9799 let mut editor = build_editor(multibuffer.clone(), cx);
9800 let snapshot = editor.snapshot(cx);
9801 editor.change_selections(None, cx, |s| {
9802 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9803 });
9804 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9805 assert_eq!(
9806 editor.selections.ranges(cx),
9807 [
9808 Point::new(1, 3)..Point::new(1, 3),
9809 Point::new(2, 1)..Point::new(2, 1),
9810 ]
9811 );
9812 editor
9813 });
9814
9815 // Refreshing selections is a no-op when excerpts haven't changed.
9816 editor.update(cx, |editor, cx| {
9817 editor.change_selections(None, cx, |s| {
9818 s.refresh();
9819 });
9820 assert_eq!(
9821 editor.selections.ranges(cx),
9822 [
9823 Point::new(1, 3)..Point::new(1, 3),
9824 Point::new(2, 1)..Point::new(2, 1),
9825 ]
9826 );
9827 });
9828
9829 multibuffer.update(cx, |multibuffer, cx| {
9830 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9831 });
9832 editor.update(cx, |editor, cx| {
9833 // Removing an excerpt causes the first selection to become degenerate.
9834 assert_eq!(
9835 editor.selections.ranges(cx),
9836 [
9837 Point::new(0, 0)..Point::new(0, 0),
9838 Point::new(0, 1)..Point::new(0, 1)
9839 ]
9840 );
9841
9842 // Refreshing selections will relocate the first selection to the original buffer
9843 // location.
9844 editor.change_selections(None, cx, |s| {
9845 s.refresh();
9846 });
9847 assert_eq!(
9848 editor.selections.ranges(cx),
9849 [
9850 Point::new(0, 1)..Point::new(0, 1),
9851 Point::new(0, 3)..Point::new(0, 3)
9852 ]
9853 );
9854 assert!(editor.selections.pending_anchor().is_some());
9855 });
9856 }
9857
9858 #[gpui::test]
9859 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9860 cx.set_global(Settings::test(cx));
9861 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9862 let mut excerpt1_id = None;
9863 let multibuffer = cx.add_model(|cx| {
9864 let mut multibuffer = MultiBuffer::new(0);
9865 excerpt1_id = multibuffer
9866 .push_excerpts(
9867 buffer.clone(),
9868 [
9869 ExcerptRange {
9870 context: Point::new(0, 0)..Point::new(1, 4),
9871 primary: None,
9872 },
9873 ExcerptRange {
9874 context: Point::new(1, 0)..Point::new(2, 4),
9875 primary: None,
9876 },
9877 ],
9878 cx,
9879 )
9880 .into_iter()
9881 .next();
9882 multibuffer
9883 });
9884 assert_eq!(
9885 multibuffer.read(cx).read(cx).text(),
9886 "aaaa\nbbbb\nbbbb\ncccc"
9887 );
9888 let (_, editor) = cx.add_window(Default::default(), |cx| {
9889 let mut editor = build_editor(multibuffer.clone(), cx);
9890 let snapshot = editor.snapshot(cx);
9891 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9892 assert_eq!(
9893 editor.selections.ranges(cx),
9894 [Point::new(1, 3)..Point::new(1, 3)]
9895 );
9896 editor
9897 });
9898
9899 multibuffer.update(cx, |multibuffer, cx| {
9900 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9901 });
9902 editor.update(cx, |editor, cx| {
9903 assert_eq!(
9904 editor.selections.ranges(cx),
9905 [Point::new(0, 0)..Point::new(0, 0)]
9906 );
9907
9908 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9909 editor.change_selections(None, cx, |s| {
9910 s.refresh();
9911 });
9912 assert_eq!(
9913 editor.selections.ranges(cx),
9914 [Point::new(0, 3)..Point::new(0, 3)]
9915 );
9916 assert!(editor.selections.pending_anchor().is_some());
9917 });
9918 }
9919
9920 #[gpui::test]
9921 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9922 cx.update(|cx| cx.set_global(Settings::test(cx)));
9923 let language = Arc::new(Language::new(
9924 LanguageConfig {
9925 brackets: vec![
9926 BracketPair {
9927 start: "{".to_string(),
9928 end: "}".to_string(),
9929 close: true,
9930 newline: true,
9931 },
9932 BracketPair {
9933 start: "/* ".to_string(),
9934 end: " */".to_string(),
9935 close: true,
9936 newline: true,
9937 },
9938 ],
9939 ..Default::default()
9940 },
9941 Some(tree_sitter_rust::language()),
9942 ));
9943
9944 let text = concat!(
9945 "{ }\n", // Suppress rustfmt
9946 " x\n", //
9947 " /* */\n", //
9948 "x\n", //
9949 "{{} }\n", //
9950 );
9951
9952 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9953 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9954 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9955 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9956 .await;
9957
9958 view.update(cx, |view, cx| {
9959 view.change_selections(None, cx, |s| {
9960 s.select_display_ranges([
9961 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9962 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9963 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9964 ])
9965 });
9966 view.newline(&Newline, cx);
9967
9968 assert_eq!(
9969 view.buffer().read(cx).read(cx).text(),
9970 concat!(
9971 "{ \n", // Suppress rustfmt
9972 "\n", //
9973 "}\n", //
9974 " x\n", //
9975 " /* \n", //
9976 " \n", //
9977 " */\n", //
9978 "x\n", //
9979 "{{} \n", //
9980 "}\n", //
9981 )
9982 );
9983 });
9984 }
9985
9986 #[gpui::test]
9987 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9988 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9989
9990 cx.set_global(Settings::test(cx));
9991 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9992
9993 editor.update(cx, |editor, cx| {
9994 struct Type1;
9995 struct Type2;
9996
9997 let buffer = buffer.read(cx).snapshot(cx);
9998
9999 let anchor_range = |range: Range<Point>| {
10000 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10001 };
10002
10003 editor.highlight_background::<Type1>(
10004 vec![
10005 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10006 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10007 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10008 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10009 ],
10010 |_| Color::red(),
10011 cx,
10012 );
10013 editor.highlight_background::<Type2>(
10014 vec![
10015 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10016 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10017 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10018 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10019 ],
10020 |_| Color::green(),
10021 cx,
10022 );
10023
10024 let snapshot = editor.snapshot(cx);
10025 let mut highlighted_ranges = editor.background_highlights_in_range(
10026 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10027 &snapshot,
10028 cx.global::<Settings>().theme.as_ref(),
10029 );
10030 // Enforce a consistent ordering based on color without relying on the ordering of the
10031 // highlight's `TypeId` which is non-deterministic.
10032 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10033 assert_eq!(
10034 highlighted_ranges,
10035 &[
10036 (
10037 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10038 Color::green(),
10039 ),
10040 (
10041 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10042 Color::green(),
10043 ),
10044 (
10045 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10046 Color::red(),
10047 ),
10048 (
10049 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10050 Color::red(),
10051 ),
10052 ]
10053 );
10054 assert_eq!(
10055 editor.background_highlights_in_range(
10056 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10057 &snapshot,
10058 cx.global::<Settings>().theme.as_ref(),
10059 ),
10060 &[(
10061 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10062 Color::red(),
10063 )]
10064 );
10065 });
10066 }
10067
10068 #[gpui::test]
10069 fn test_following(cx: &mut gpui::MutableAppContext) {
10070 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10071
10072 cx.set_global(Settings::test(cx));
10073
10074 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10075 let (_, follower) = cx.add_window(
10076 WindowOptions {
10077 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10078 ..Default::default()
10079 },
10080 |cx| build_editor(buffer.clone(), cx),
10081 );
10082
10083 let pending_update = Rc::new(RefCell::new(None));
10084 follower.update(cx, {
10085 let update = pending_update.clone();
10086 |_, cx| {
10087 cx.subscribe(&leader, move |_, leader, event, cx| {
10088 leader
10089 .read(cx)
10090 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10091 })
10092 .detach();
10093 }
10094 });
10095
10096 // Update the selections only
10097 leader.update(cx, |leader, cx| {
10098 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10099 });
10100 follower.update(cx, |follower, cx| {
10101 follower
10102 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10103 .unwrap();
10104 });
10105 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10106
10107 // Update the scroll position only
10108 leader.update(cx, |leader, cx| {
10109 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10110 });
10111 follower.update(cx, |follower, cx| {
10112 follower
10113 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10114 .unwrap();
10115 });
10116 assert_eq!(
10117 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10118 vec2f(1.5, 3.5)
10119 );
10120
10121 // Update the selections and scroll position
10122 leader.update(cx, |leader, cx| {
10123 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10124 leader.request_autoscroll(Autoscroll::Newest, cx);
10125 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10126 });
10127 follower.update(cx, |follower, cx| {
10128 let initial_scroll_position = follower.scroll_position(cx);
10129 follower
10130 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10131 .unwrap();
10132 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10133 assert!(follower.autoscroll_request.is_some());
10134 });
10135 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10136
10137 // Creating a pending selection that precedes another selection
10138 leader.update(cx, |leader, cx| {
10139 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10140 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10141 });
10142 follower.update(cx, |follower, cx| {
10143 follower
10144 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10145 .unwrap();
10146 });
10147 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10148
10149 // Extend the pending selection so that it surrounds another selection
10150 leader.update(cx, |leader, cx| {
10151 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10152 });
10153 follower.update(cx, |follower, cx| {
10154 follower
10155 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10156 .unwrap();
10157 });
10158 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10159 }
10160
10161 #[test]
10162 fn test_combine_syntax_and_fuzzy_match_highlights() {
10163 let string = "abcdefghijklmnop";
10164 let syntax_ranges = [
10165 (
10166 0..3,
10167 HighlightStyle {
10168 color: Some(Color::red()),
10169 ..Default::default()
10170 },
10171 ),
10172 (
10173 4..8,
10174 HighlightStyle {
10175 color: Some(Color::green()),
10176 ..Default::default()
10177 },
10178 ),
10179 ];
10180 let match_indices = [4, 6, 7, 8];
10181 assert_eq!(
10182 combine_syntax_and_fuzzy_match_highlights(
10183 &string,
10184 Default::default(),
10185 syntax_ranges.into_iter(),
10186 &match_indices,
10187 ),
10188 &[
10189 (
10190 0..3,
10191 HighlightStyle {
10192 color: Some(Color::red()),
10193 ..Default::default()
10194 },
10195 ),
10196 (
10197 4..5,
10198 HighlightStyle {
10199 color: Some(Color::green()),
10200 weight: Some(fonts::Weight::BOLD),
10201 ..Default::default()
10202 },
10203 ),
10204 (
10205 5..6,
10206 HighlightStyle {
10207 color: Some(Color::green()),
10208 ..Default::default()
10209 },
10210 ),
10211 (
10212 6..8,
10213 HighlightStyle {
10214 color: Some(Color::green()),
10215 weight: Some(fonts::Weight::BOLD),
10216 ..Default::default()
10217 },
10218 ),
10219 (
10220 8..9,
10221 HighlightStyle {
10222 weight: Some(fonts::Weight::BOLD),
10223 ..Default::default()
10224 },
10225 ),
10226 ]
10227 );
10228 }
10229
10230 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10231 let point = DisplayPoint::new(row as u32, column as u32);
10232 point..point
10233 }
10234
10235 fn assert_selection_ranges(
10236 marked_text: &str,
10237 selection_marker_pairs: Vec<(char, char)>,
10238 view: &mut Editor,
10239 cx: &mut ViewContext<Editor>,
10240 ) {
10241 let snapshot = view.snapshot(cx).display_snapshot;
10242 let mut marker_chars = Vec::new();
10243 for (start, end) in selection_marker_pairs.iter() {
10244 marker_chars.push(*start);
10245 marker_chars.push(*end);
10246 }
10247 let (_, markers) = marked_text_by(marked_text, marker_chars);
10248 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10249 .iter()
10250 .map(|(start, end)| {
10251 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10252 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10253 start..end
10254 })
10255 .collect();
10256 assert_eq!(
10257 view.selections.display_ranges(cx),
10258 &asserted_ranges[..],
10259 "Assert selections are {}",
10260 marked_text
10261 );
10262 }
10263}
10264
10265trait RangeExt<T> {
10266 fn sorted(&self) -> Range<T>;
10267 fn to_inclusive(&self) -> RangeInclusive<T>;
10268}
10269
10270impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10271 fn sorted(&self) -> Self {
10272 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10273 }
10274
10275 fn to_inclusive(&self) -> RangeInclusive<T> {
10276 self.start.clone()..=self.end.clone()
10277 }
10278}